依赖收集、派发更新和Vue.set

2020-03-12  本文已影响0人  大猪蹄子_0f6b

defineReactive给数据添加了 getter 和 setter。
在defineReactive内第一步是实例了Dep

Dep是整个 getter 依赖收集的核⼼

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;
  constructor () {
    this.id = uid++
    this.subs = []
  }
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }
  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

target 是⼀个全局唯⼀ Watcher ,这是⼀个⾮常巧妙的设计,因为在同⼀时间只能有⼀个全局的 Watcher 被计算,另外它的⾃⾝属性 subs 也是 Watcher 的数组。

Watcher

这个Class的构造函数中定义了一些和Dep相关的属性。

this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()

依赖收集

  //defineReactive方法内部
  let childOb = !shallow && observe(val)//可能还是个obj
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()// 执行Watcher ? Dep.target.addDep(this)
        if (childOb) {
          childOb.dep.depend()
          //收集了依赖
          //执⾏ Vue.set 的时候通过 ob.dep.notify() 能够通知到 watcher
          //从⽽让添加新的属性到对象也可以检测到变化
          if (Array.isArray(value)) {
            dependArray(value)//把数组每个元素也去做依赖收集
          }
        }
      }
      return value
    }
    //...
  })

在mount过程中,mountComponent函数有这么一段逻辑

updateComponent = () => {
  vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
  before () {
    if (vm._isMounted) {
      callHook(vm, 'beforeUpdate')
    }
  }
}, true /* isRenderWatcher */)

派发更新

setter 的逻辑有 2 个关键的点,⼀个是 childOb = !shallow && observe(newVal) ,如果 shallow为 false 的情况,会对新设置的值变成⼀个响应式对象;另⼀个是 dep.notify() ,通知所有的订阅者

  //defineReactive方法内部
  let childOb = !shallow && observe(val)//可能还是个obj
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
    //...
  })

在组件中对响应的数据做了修改,就会触发 setter 的逻辑,最终调用dep.notify(),即遍历subs调用每个watcher的update方法,会对watcher不同状态走不同逻辑,一般情况下会执行queueWatcher逻辑

针对特殊情况

给响应式对象添加新属性,使用Vue.set API

set ⽅法接收 3个参数, target 可能是数组或者是普通对象, key 代表的是数组的下标或者是对象的键值, val 代表添加的值。

上一篇下一篇

猜你喜欢

热点阅读