Vue2-响应式系统完善

文章讨论了在Vue2的响应式系统中,当Watcher不再依赖特定属性时从Dep中移除Watcher导致遍历subs数组出错的问题。解决方案是在notify方法中先复制subs数组,确保遍历时的安全。这样,即使在循环中移除Watcher,也不会影响后续的迭代过程。
摘要由CSDN通过智能技术生成

场景

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: "hello, world",
};
observe(data);
let show = true;
const updateComponent = () => {
    if (show) {
        console.log(data.text);
        show = false;
    }
};

new Watcher(updateComponent);

new Watcher(() => console.log("依赖", data.text));

data.text = "123";

先可以 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 。

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剥丝抽茧-响应式系统之分支切换 (opens new window)这篇文章里我们做了什么。
在这里插入图片描述
如果 Watcher 中的函数不再依赖当前属性,我们就把当前 Watcher 从该属性的 Dep 中移除。

而移除其实就是调用了数组的 splice 方法,直接将 Dep 中的 subs 数组元素进行删除。

removeSub(sub) {
  remove(this.subs, sub);
}

export function remove(arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item);
    if (index > -1) {
      return arr.splice(index, 1);
    }
  }
}

而此时我们正在遍历 subs 数组:

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

对应上边的例子,原本 subs 数组两个 Watcher,第一个 Watcher 执行的时候没有访问 data.text 属性,就要把这一个 Watcher 删除了,第二个就移动到第一个的位置了,此时 for 循环中访问第二个位置的 Watcher 因为被移到前边自然就报错了。

修改起来也很容易,我们只需要在循环前,将原有的 subs 数组保存给一个新的数组即可。

notify() {
  // stabilize the subscriber list first
  const subs = this.subs.slice();
  for (let i = 0, l = subs.length; i < l; i++) {
    subs[i].update();
  }
}

这篇文章比较简单,主要就是循环通知 Watcher 之前把列表另存起来,防止遍历过程中被修改。

转载于:
https://vue.windliang.wang/
文章源码来源于:
https://github.com/wind-liang/vue2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值