Vue.js 源码分析- 数据响应式原理-依赖收集

368 阅读3分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战

数据响应式原理-依赖收集

依赖收集其实就是把依赖该属性的watcher对象添加到 dep 对象的 subs 数组中,当依赖数据发生变化的时候通知所有的watcher

何时收集依赖?

何时收集依赖,这个时候我们需要进行一些逆向思维,什么时候需要使这个数据及时更新?

  • 这个数据被视图层使用的时候 那么这样就很简单了,我们就可以在读取的时候进行对应的依赖收集,也就是Object.definePropertyget的时候
get: function reactiveGetter () {
  // 如果预定义的 getter 存在则 value 等于getter 调用的返回值
  // 否则直接赋予属性值
  const value = getter ? getter.call(obj) : val
  // 如果存在当前依赖目标,即 watcher 对象,则建立依赖
  if (Dep.target) {
    dep.depend()
    // 如果子观察目标存在,建立子对象的依赖关系
    if (childOb) {
      childOb.dep.depend()
      // 如果属性是数组,则特殊处理收集数组对象依赖
      if (Array.isArray(value)) {
        dependArray(value)
      }
    }
  }
  // 返回属性值
  return value
}

Dep.target 属性何时初始化?

之前我们进行模拟的时候也有提到 模拟,Dep.targe就是在我们创建 watcher 类的时候进行初始化的

那我们先查看

  • watcher类:
  • 路径:src\core\observer\watcher.js 我们可以看见 构造函数中
 vm._watchers.push(this)

将 watcher 对象 存储到了vue_watchers数组中

接着,我们还可以看见这样一段代码:

this.value = this.lazy
      ? undefined
      : this.get()

重头戏来了

  • get 方法: 这里我们调用了 pushTarget 方法并且将 watcher 实例传入
get () {
    pushTarget(this)
    ...
}
  • pushTarget: 这里为 Dep.target = target, 并且值就是我们的 watcher 对象
Dep.target = null
const targetStack = []
// 入栈并将当前 watcher 赋值给 Dep.target
// 父子组件嵌套的时候先把父组件对应的 watcher 入栈,
// 再去处理子组件的 watcher,子组件的处理完毕后,再把父组件对应的 watcher 出栈,继续操作
export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

Dep.target 用来存放目前正在使用的watcher 全局唯一,并且一次也只能有一个watcher被使用

dep.depend() 收集依赖

// 将观察对象和 watcher 建立依赖
  depend () {
    if (Dep.target) {
      // 如果 target 存在,把 dep 对象添加到 watcher 的依赖中
      Dep.target.addDep(this)
    }
  }

这里我们可以看见我们调用了 Dep.target.addDep(this),加上我们上面的了解target就是watcher,所以我们要了解addDep就有需要回到watcher类中了

addDep

路径: src\core\observer\watcher.js

addDep (dep: Dep) {
    const id = dep.id
    // 判断当前是否存储了 这个对象
    if (!this.newDepIds.has(id)) {
      // 如果没有的话 将数据存储到 watcher 的集合中
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      // 最后会把 watcher 这个对象 添加到 dep的subs数组中
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }

这个方法很简单,首先 判断当前是否存储了这个对象,如果没有的话 将数据存储到 watcher 的集合中,最后会把 watcher 这个对象 添加到 dep的subs数组中

总结

  1. watcherget方法中,会给Dep.target进行赋值操作
  2. Dep.target存在之后,我们会调用 dep.depend()这个方法,这个方法最终是要把 watcher 这个对象 添加到 dep的subs数组中