接 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
6const 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
之前把列表另存起来,防止遍历过程中被修改。