接 Vue2剥丝抽茧-响应式系统、Vue2剥丝抽茧-响应式系统之分支切换,Vue2剥丝抽茧-响应式系统之嵌套 还没有看过的同学需要看一下。
这篇文章主要修之前代码存在的一个问题,废话不多说,上代码!
场景
| 1 | import { observe } from "./reactive"; | 
先可以 1 分钟思考一下会输出什么。
- new Watcher(updateParentComponent);- 执行 - updateParentComponent函数,输出- hello, world,并且- text的- Dep收集该- Watcher。 
- new Watcher(() => console.log("依赖", data.text));- 执行匿名函数,输出 - 依赖 hello, world,并且- text的- Dep收集该- Watcher。 
- data.text = "123";。- 触发 - text的- set,依次执行- Dep中的- Watcher。- 先执行 - updateParentComponent。- 1 
 2
 3
 4
 5
 6- const updateComponent = () => { 
 if (show) {
 console.log(data.text);
 show = false;
 }
 };- 由于之前已经执行过一次了,此时 - show就是- false了,什么都不会输出。- 再执行 - () => console.log("依赖", data.text),输出- 依赖 hello, world。
是的,上边是我们所期望的样子,但事实上输出结果如下:

出错代码 dep.js:37:26 如下:

调用 update 的时候是,遍历过程中 subs[i] 变成了 undefined ,导致了报错。
需要回忆下 Vue2剥丝抽茧-响应式系统之分支切换 这篇文章里我们做了什么。

如果 Watcher 中的函数不再依赖当前属性,我们就把当前 Watcher 从该属性的 Dep 中移除。
而移除其实就是调用了数组的 splice 方法,直接将 Dep 中的 subs 数组元素进行删除。
| 1 | removeSub(sub) { | 
而此时我们正在遍历 subs 数组:
| 1 | notify() { | 
对应上边的例子,原本 subs 数组两个 Watcher,第一个 Watcher 执行的时候没有访问 data.text 属性,就要把这一个 Watcher 删除了,第二个就移动到第一个的位置了,此时 for 循环中访问第二个位置的 Watcher 因为被移到前边自然就报错了。
修改起来也很容易,我们只需要在循环前,将原有的 subs 数组保存给一个新的数组即可。
| 1 | notify() { | 
总
这篇文章比较简单,主要就是循环通知 Watcher 之前把列表另存起来,防止遍历过程中被修改。
