背景

仔细阅读VuejsWatcher.js,发现他不仅维护了一个deps以及和他相关的depIds。还维护了一个newDepsnewDepIds。为什么要维护两个dep数组呢?

答疑

这其实是为了移除掉那些不需要观察的数据,也就是说这个watcher不需要再关心某些数据是否变动了,这样说可能抽象,举个例子:

  1. computed: {
  2. value() {
  3. //假设this.sign一开始为true.
  4. if(this.sign) {
  5. return this.list
  6. } else {
  7. return this.anotherList
  8. }
  9. }

我定义了一个computed,那么它对应的watcher实例,应该观察哪些数据呢,或者说应该订阅哪些数据呢?
首先思考他读取了什么:首先读取了**sign**,发现**sign****true**的情况下,那么会读取**list**. 所以这个**watcher**实例应该进入**sign****list**属性对应的**dep**实例。也就是此时**watcher**实例的**deps**中也应该是这两个**dep**组成的数组。
他会进入**anotherList**吗?答案是否定的,因为并没有读取那个数据。


做一个假设:我将this.sign改成false。此时肯定会触发Watcher实例的update操作,就会导致重新执行get操作: :::info 这次,他又会读取**sign**,接着读取**anotherList**,导致**watcher**实例应该又多了一个成员:**anotherList**对应的dep。 ::: 细想一下,**watcher**实例还需要观察**list**数据,当然不需要。不管**list**怎么改变,这个**computed**的结果根本不会收到任何影响,所以这个**Watcher**实例不应该观察这个**list**数据了:**list**对应的**dep**应该将这个**watcher**移除,而**watcher**实例中的**deps**也应该将**list**对应的**dep**移除。
**newDepIds****newDeps**就是用来完成这件事的。

newDepIds和newDeps完成的任务

实际上,**newDeps****newDepIds**就是这次**get**执行收集到的依赖。
而,**deps****depIds**是上次**get执行收集到的依赖。**
对应上面的🌰那就是:

  1. //伪代码
  2. deps = [’sign‘, 'list'];
  3. newDeps = ['sign', 'anotherList'];

还是看源码,depsnewDeps在源码中是如何被使用的:
这是在添加依赖时:

  1. addDep (dep: Dep) {
  2. const id = dep.id
  3. if (!this.newDepIds.has(id)) {
  4. //当newDepIds中不存在这个dep时,才收集该dep。
  5. this.newDepIds.add(id)
  6. this.newDeps.push(dep)
  7. //如果depIds存在这个dep。说明这个dep已经收集了这个watcher了
  8. //就没必要反复收集了。
  9. if (!this.depIds.has(id)) {
  10. dep.addSub(this)
  11. }
  12. }
  13. }

这是cleanupDeps方法,在get方法收集完依赖之后,这个时候需要做一次依赖的清理,使得已经不需要观察的数据取消观察:

  1. cleanupDeps () {
  2. let i = this.deps.length
  3. //遍历原来收集过的依赖
  4. while (i--) {
  5. const dep = this.deps[i]
  6. //不存在于newDepIds中,说明都是被取消观察的数据。
  7. if (!this.newDepIds.has(dep.id)) {
  8. //针对这些依赖,执行removeSub,从而将这个watcher
  9. //移除出依赖列表。
  10. dep.removeSub(this)
  11. }
  12. }
  13. //交换depIds和newDepIds的值
  14. let tmp = this.depIds
  15. this.depIds = this.newDepIds
  16. this.newDepIds = tmp
  17. //清除newDepIds,以备下次再用
  18. this.newDepIds.clear()
  19. //交换deps和newDeps的值
  20. //我们前面只是从dep中移除了watcher。watcher中还有这个dep。
  21. //我们也应该从watcher中移除这个不需要维护的dep。执行下面的
  22. //交换值操作即可。
  23. tmp = this.deps
  24. this.deps = this.newDeps
  25. this.newDeps = tmp
  26. //清除newDepIds,以备下次再用
  27. this.newDeps.length = 0
  28. }