V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jzlivioo
V2EX  ›  程序员

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

  •  
  •   jzlivioo · 2021-12-10 16:56:38 +08:00 · 1388 次点击
    这是一个创建于 1083 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在看 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 ?晕在了上面这段逻辑

    6 条回复    2021-12-10 17:52:52 +08:00
    ryncv
        1
    ryncv  
       2021-12-10 17:11:26 +08:00
    addDep 是将当前 watcher 收集到 dep 中。
    关键在这一句:dep.addSub(this);
    tyx1703
        2
    tyx1703  
       2021-12-10 17:15:46 +08:00
    dep 记录了订阅者列表,也就是 watcher 。当 watcher 销毁时,要把它自己从 dep 的订阅者列表中移除,来避免下次 dep 更新时,再来通知这个已经销毁的 watcher 。
    jzlivioo
        3
    jzlivioo  
    OP
       2021-12-10 17:16:37 +08:00
    @ryncv 可是 addSub 是 Dep 用来收集 Watcher 的,为什么 Watcher 收集 Dep 的方法中会调用这个?
    RyanLim
        4
    RyanLim  
       2021-12-10 17:27:52 +08:00
    @tyx1703 +1 ,不能说是相互收集,可以理解为类似,双向链表,要断链表的话需要 parent.removeChild ,children.removeParent 。
    ryncv
        5
    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
        6
    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 循环通知

    //个人理解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6005 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 02:32 · PVG 10:32 · LAX 18:32 · JFK 21:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.