当前位置:首页 > 域名

Vue2剥丝抽茧-响应式系统之分支切换

场景

我们考虑一下下边的丝抽代码会输出什么。

import { observe } from "./reactive";

import Watcher from "./watcher";

const data = {

text: "hello,茧响 world",

ok: true,

};

observe(data);

const updateComponent = () => {

console.log("收到", data.ok ? data.text : "not");

};

new Watcher(updateComponent); // updateComponent 执行一次函数,输出 hello,应式 world

data.ok = false; // updateComponent 执行一次函数,输出 not

data.text = "hello,系统 liang"; // updateComponent 会执行吗?

我们来一步一步理清:

observer(data)

拦截了 data 中 text 和 ok 的 get、set,分支切并且各自初始化了一个 Dep 实例,丝抽用来保存依赖它们的茧响 Watcher 对象。

new Watcher(updateComponent);

这一步会执行 updateComponent 函数,应式执行过程中用到的系统所有对象属性,会将 Watcher 收集到相应对象属性中的分支切Dep 中。

当然这里的丝抽 Watcher 其实是同一个,所以用了指向的茧响箭头。

data.ok = false;

这一步会触发 set ,应式从而执行 Dep 中所有的系统 Watcher ,此时就会执行一次 updateComponent 。分支切

执行 updateComponent 就会重新读取 data 中的属性,触发 get,然后继续收集 Watcher 。

重新执行 updateComponent 函数 的时候:

const updateComponent = () => {

console.log("收到", data.ok ? data.text : "not");

};

因为 data.ok 的值变为 false ,所以就不会触发 data.text 的 get ,text 的云服务器提供商 Dep 就不会变化了。

而 data.ok 会继续执行,触发 get 收集 Watcher ,但由于我们 Dep 中使用的是数组,此时收集到的两个 Wacher 其实是同一个,这里是有问题,会导致 updateComponent 重复执行,一会儿我们来解决下。

data.text = "hello, liang";

执行这句的时候,会触发 text 的 set,所以会执行一次 updateComponent 。但从代码来看 updateComponent 函数中由于 data.ok 为 false,data.text 对输出没有任何影响,这次执行其实是没有必要的。

之所以执行了,是因为第一次执行 updateComponent 读取了 data.text 从而收集了 Watcher ,第二次执行 updateComponent 的时候,data.text 虽然没有读到,但之前的 Watcher 也没有清除掉,所以这一次改变 data.text 的时候 updateComponent 依旧会执行。网站模板

所以我们需要的就是当重新执行 updateComponent 的时候,如果 Watcher 已经不依赖于某个 Dep 了,我们需要将当前 Watcher 从该 Dep 中移除掉。

问题

总结下来我们需要做两件事情。

去重,Dep 中不要重复收集 Watcher 。重置,如果该属性对 Dep 中的 Wacher 已经没有影响了(换句话就是,Watcher 中的 updateComponent 已经不会读取到该属性了 ),就将该 Watcher 从该属性的 Dep 中删除。

去重

去重的话有两种方案:

Dep 中的 subs 数组换为 Set。每个 Dep 对象引入 id ,Watcher 对象中记录所有的 Dep 的 id,下次重新收集依赖的时候,如果 Dep 的 id 已经存在,就不再收集该 Watcher 了。

Vue2 源码中采用的是方案 2 这里我们实现下:

Dep 类的话只需要引入 id 即可。云南idc服务商

/

分享到:

滇ICP备2023006006号-16