求助, Vue 的观察者模式里, Dep 和 Watcher 是如何确立对应关系(依赖收集)的?

2021-12-10 16:56:38 +08:00
 jzlivioo

在看 Vue 源码的时候发现很困惑的一点,Dep 会收集 Watcher ,然后提供出方法来执行所收集 Watcher 的 update 方法

src/core/observer/dep

export default class Dep {
  static target: ?Watcher;
  ...

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  notify () {
    ...
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
  ...
}

看到这没什么问题,但在我继续往下看,想了解 Dep 如何收集 Watcher 的时候,我陷入了怪圈

看的路径大致如下:

想知道:Dep 实例的 addSub ==> 找到了 Wacher 实例里有个方法 addDep ,会调用 addSub(this)进行赋值

??因为 Watcher 也会收集 Dep ,这时候我开始有点逻辑混乱,为什么要收集 Dep ,算了先不管

找什么地方用了 Watcher 的 addDep ==> 找到了 Dep 的 depend 方法,如上面代码所示

继续找 Dep 的 depend 方法 ==> 在 Watcher 里找到了。。。把我套死在这了

export default class Watcher {
  ...
  addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }
  depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }
}

后面以 Dep.target 为突破口找到了 component mounted 里会 new 一个 Watcher, 勉强理解了视图 Watcher 怎么添加的,大概是 new Watcher+访问被添加了响应式的数据(全靠猜)

但是 Watcher 为什么要收集 Dep ?晕在了上面这段逻辑

1276 次点击
所在节点    程序员
6 条回复
ryncv
2021-12-10 17:11:26 +08:00
addDep 是将当前 watcher 收集到 dep 中。
关键在这一句:dep.addSub(this);
tyx1703
2021-12-10 17:15:46 +08:00
dep 记录了订阅者列表,也就是 watcher 。当 watcher 销毁时,要把它自己从 dep 的订阅者列表中移除,来避免下次 dep 更新时,再来通知这个已经销毁的 watcher 。
jzlivioo
2021-12-10 17:16:37 +08:00
@ryncv 可是 addSub 是 Dep 用来收集 Watcher 的,为什么 Watcher 收集 Dep 的方法中会调用这个?
RyanLim
2021-12-10 17:27:52 +08:00
@tyx1703 +1 ,不能说是相互收集,可以理解为类似,双向链表,要断链表的话需要 parent.removeChild ,children.removeParent 。
ryncv
2021-12-10 17:49:46 +08:00
如果你说的是 newDepIds 那行判断的话,主要是为了避免收集重复依赖,比如在模板中写了两个{{user}}{{user}},直接 addSub 就会被重复收集一次。
详细的可以看看这里 http://caibaojian.com/vue-design/art/8vue-reactive-dep-watch.html#%E5%88%9D%E8%AF%86-watcher
violetlai
2021-12-10 17:52:52 +08:00
大脑里面走一遍流程
1.第一遍在 defineReactive 第一次实例化了 dep
2.编译 compile 的时候走到了 new Watcher 然后走到了 addDep 这时候给 target 赋值把自己( Watcher 实例)添加到 Dep.target 这个静态方法里面 但是没有更改属性 就没有添加 这个 watcher 就没有办法循环通知
if (Dep.target) {// false
Dep.target.addDep(this)
}
3.改变了属性 if (Dep.target) {// true
Dep.target.addDep(this)
}
添加到了 dep 里面 然后触发 set 循环通知

//个人理解

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/821381

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX