Vue2与Vue3响应式原理分析

89 阅读1分钟

Vue2

数据劫持(Object.defineProperty)+发布订阅模式

1.Vue在初始化的时候需要通过Object.defineProperty 将data里的属性变成具有getter、setter可响应的

2.Vue 在$mount中的会实例化一个 Watcher 实例,实例化过程中会触发 get() 将Watcher实例指向 Dep.target;

3.在渲染过程中使用data中的属性会触发该属性的 getter,进而会触发 dep.depend() 进行依赖收集;闭包的dep实例的subs添加观察它的 Watcher 实例;Watcher 的deps中添加dep;

4.当data的某个属性值变化后,会触发该属性的setter,dep.notify() 派发更新;触发dep.subs中Watcher实例执行 update() 方法,最后实际上是调用Watcher的回调函数cb,进而更新视图。

image.png

Vue3

// 存储副作用函数的桶
const bucket = new WeakMap()
// 响应式
function reactive(obj){
  return new Proxy(obj, {
    // 拦截读取操作
    get(target, key) {
      // 将副作用函数 activeEffect 添加到存储副作用函数的桶中
      track(target, key)
      // 返回属性值
      return Reflect.get(target,key)
    },
    // 拦截设置操作
    set(target, key, newVal) {
      // 设置属性值
      Reflect.set(target,key,newVal)
      // 把副作用函数从桶里取出并执行
      trigger(target, key)
    }
  })
}
// 收集依赖
function track(target, key) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  deps.add(activeEffect)
}
// 派发更新
function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  effects && effects.forEach(fn => fn())
}
// 用一个全局变量存储当前激活的 effect 函数
let activeEffect
function effect(fn) {
  // 当调用 effect 注册副作用函数时,将副作用函数复制给 activeEffect
  activeEffect = fn
  // 执行副作用函数
  fn()
}