响应式原理(Vue3、Vue2)

1.Vue3

副作用函数(onMounted、watchEffect)

帮助管理组件中的副作用逻辑,并自动追踪其依赖关系,以确保在数据变化时能够自动触发副作用函数的更新。

会自动追踪被其内部函数引用的响应式数据。当这些数据发生变化时,Vue 3 会自动重新运行副作用函数,确保副作用与数据的状态保持同步。

effect就是组件的setup,我们定义ref,操作数据都是在里面实现的,所以具有了响应式的效果:在初始化进行依赖收集,在数据变化时重新触发effect执行

let activeEffect
// 副作用函数
export const effect = (fn) => {
    const _effect = function () {
        activeEffect = _effect
        fn()
    }
    _effect()
}

 

收集依赖

通过proxy里的调用收集依赖

 

 

// 收集依赖
const targetMap = new WeakMap()
export const track = (target, key) => {
    // WeakMap 
  // key为对象, 即target
  // value 为一个new Map()
    // {
    //   key:{
    //     name: "名字",
    //     age: 18
    //   },
    //   value: {
    //     name: new Map(),
    //     age: new Map()
    //   }
    // }
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    // 现在添加new Map()
  // key为属性名称, 即key
  // value为一个new Set()
    let deps = depsMap.get(key)
    if (!deps) {
        deps = new Set()
        depsMap.set(key, deps)
    }
  // 现在添加Set()
  // 给Set添加副作用函数
    deps.add(activeEffect)
}

 

更新依赖

// 更新依赖
export const trigger = (target, key) => {
  // 根据组件的响应式组件对象,拿到对应的map
    const depsMap = targetMap.get(target)
  // 通过具体key, 拿到对应副作用函数
    const deps = depsMap.get(key)
  // 执行
    deps.forEach((effect) => effect())
}

 

完整代码(effect.js)

let activeEffect
// 副作用函数
export const effect = (fn) => {
    const _effect = function () {
        activeEffect = _effect
        fn()
    }
    _effect()
}
// 收集依赖
const targetMap = new WeakMap()
export const track = (target, key) => {
    // WeakMap 
  // key为对象, 即target
  // value 为一个new Map()
    // {
    //   key:{
    //     name: "名字",
    //     age: 18
    //   },
    //   value: {
    //     name: new Map(),
    //     age: new Map()
    //   }
    // }
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    // 现在添加new Map()
  // key为属性名称, 即key
  // value为一个new Set()
    let deps = depsMap.get(key)
    if (!deps) {
        deps = new Set()
        depsMap.set(key, deps)
    }
  // 现在添加Set()
  // 给Set添加副作用函数
    deps.add(activeEffect)
}

// 更新依赖
export const trigger = (target, key) => {
  // 根据组件的响应式组件对象,拿到对应的map
    const depsMap = targetMap.get(target)
  // 通过具体key, 拿到对应副作用函数
    const deps = depsMap.get(key)
  // 执行
    deps.forEach((effect) => effect())
}

 

reactive实现(reactive.js)

import { track, trigger } from './effect.js'
const isObject = (target) => target != null && typeof target == 'object'
export const reactive = (target) => {
    return new Proxy(target, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
      // 收集依赖
            track(target, key)
      // 递归对象
            if (isObject(res)) {
                return reactive(res)
            }
            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
      // 更新依赖
            trigger(target, key)
            return res
        },
    })
}

 

调用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

  <div id="app">

  </div>

  <script type="module">
    import { reactive } from './reactive.js'
    import { effect } from './effect.js'
    const user = reactive({
      name: "名字",
      age: 18,
      foo: {
        bar: {
          sss: 123
        }
      }
    })
    effect(() => {
      document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
    })

    setTimeout(() => {
      user.name = '名字666'
      setTimeout(() => {
        user.age = '23'
        setTimeout(() => {
          user.foo.bar.sss = 66666666
        }, 3000)
      }, 2000)
    }, 1000)

  </script>
</body>

</html>

 

2.Vue2

数据驱动视图
1.使用Object.defineProperty数据劫持所有的属性(递归);
2.通过消息订阅器Dep,在收集依赖(push),更新依赖进行统一的操作(通知订阅者Watcher)
视图驱动数据
1.解析根节点dom元素(绑定的),创建一个fragment(不影响原dom);
2.对dom的节点进行遍历,然后塞进fragment里
3.将fragment里的childNodes进行递归遍历;判断是元素节点还是{{}}写法的文本节点;
4.{{}}写法的文本节点,绑定更新函数的订阅器Watcher处理按照之前的数据驱动视图
5.元素节点判断是v-model还是v-on:其他的或者@开始的等等;
6.若是v-model的将数据绑定更新函数的订阅器Watcher,然后给这个node绑定监听事件,若更新则会执行数据驱动视图
7.@的事件则是回调那个方法即可

posted on 2024-04-28 11:06  sss大辉  阅读(24)  评论(0编辑  收藏  举报

导航