code-for-vue-3-book
code-for-vue-3-book copied to clipboard
【55页】4.5嵌套的effect与effect栈
在进行嵌套定义effect时,如果多次修改外部effect中使用的响应式数据,会间接调用内部effect。
let data = {foo: true, bar: false, text: "data.text"};
const bucket = new WeakMap;
const effectStack = [];
effectStack.activeEffect = function () {
if (this.length) return this[this.length - 1];
return undefined;
}
let obj = new Proxy(data, {
set(target, key, newValue) {
target[key] = newValue;
trigger(target, key);
return true;
},
get(target, key) {
track(target, key);
return target[key];
}
});
function track(target, key) {
if (!effectStack.activeEffect()) return;
let depsMap = bucket.get(target);
if (!depsMap) bucket.set(target, (depsMap = new Map));
let deps = depsMap.get(key);
if (!deps) depsMap.set(key, (deps = new Set));
deps.add(effectStack.activeEffect());
effectStack.activeEffect().deps.push(deps);
}
function trigger(target, key) {
let depsMap = bucket.get(target);
if (!depsMap) return;
let effectsToRun = new Set(depsMap.get(key));
effectsToRun.forEach(fn => fn());
}
function effect(fn) {
function effectFn() {
cleanup(effectFn);
effectStack.push(effectFn);
fn();
effectStack.pop();
}
effectFn.sourceFn = fn;
effectFn.deps = [];
effectFn();
}
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
let deps = effectFn.deps[i];
deps.delete(effectFn);
}
effectFn.deps = [];
}
function line(text = "") {
let cnt = 15;
console.log("--" + text + "-".repeat(cnt));
}
line("init")
effect(function () {
console.log(`outer effect:obj.foo=${obj.foo}`);
effect(function () {
console.log(`inner effect:obj.bar=${obj.bar}`);
});
});
line("let obj.foo be false");
obj.foo = false;
obj.foo = false;
line("let obj.bar be true");
obj.bar = true;
-
effect(fn)
会将fn
包装为effectFn
- 修改
obj.foo
进行重新绑定时,会导致内部的依赖再次绑定 - 由于step1,所以
bucket
的数据结构中的Set无法对effectFn
进行去重,也就导致注册多个重复的依赖。
程序的输出结果为:
--init---------------
outer effect:obj.foo=true
inner effect:obj.bar=false
--let obj.foo be false---------------
outer effect:obj.foo=false
inner effect:obj.bar=false
outer effect:obj.foo=false
inner effect:obj.bar=false
--let obj.bar be true---------------
inner effect:obj.bar=true
inner effect:obj.bar=true
inner effect:obj.bar=true
在对内部的响应式数据进行修改时,触发了三个effectFn
,其中有初始化的1个,两次对obj.foo
进行修改时,间接添加的effectFn
。
在我个人看来,应该只需要执行一个effectFn
,而不是三个,不知道是本意如此,还是意外的BUG。
如果是BUG,我想到了一个不是很好解决的方法:通过将函数转为string来判断是否为相同的函数。例如:
let set = new Set();
function effect(fn) {
function effectFn() {
fn();
}
set.add(fn.toString());
effectFn();
}
effect(() => console.log(1));
effect(() => console.log(1));
console.log(set);
涉及修改的代码如下:
function track(target, key) {
if (!effectStack.activeEffect()) return;
let depsMap = bucket.get(target);
if (!depsMap) bucket.set(target, (depsMap = new Map));
let deps = depsMap.get(key);
if (!deps) depsMap.set(key, (deps = new Map));
//以函数文本作为key,达到去重效果
deps.set(effectStack.source, effectStack.activeEffect());
effectStack.activeEffect().deps.push(deps);
}
function trigger(target, key) {
let depsMap = bucket.get(target);
if (!depsMap) return;
let effectsToRun = new Map(depsMap.get(key));
effectsToRun.forEach(fn => fn());
}
function effect(fn) {
function effectFn() {
cleanup(effectFn);
effectStack.push(effectFn);
fn();
effectStack.pop();
}
//存储函数文本
effectFn.source = fn.toString();
effectFn.deps = [];
effectFn();
}
以上是个人看法,如有错误,还请霍老师指正😳。
往后看哦,在调度执行一节有讲
谢谢霍老师😳