4.2 分支切换与cleanup
1、分支切换
01 const data = { ok: true, text: 'hello world' }
02 const obj = new Proxy(data, { /* ... */ })
03
04 effect(function effectFn() {
05 document.body.innerText = obj.ok ? obj.text : 'not'
06 })
什么是分支切换?就是
document.body.innerText = obj.ok ? obj.text : 'not'
但是这就会导致一个问题“可能会产生遗留的副作用函数”
eg:当obj.ok的值为false的时候,obj.text不会被读取,只会触发obj.ok的读取操作。所以effectFn不应该被obj.text所收集
但是我们目前还是会遗留副作用函数,导致一些不必要的更新
如何解决???
3、清除函数
每次副作用函数执行时,我们可以先把它从所有与之关联的依赖集合中删除
当副作用函数执行完之后会重新建立连接,但在新的连接中是不会包含遗留的副作用函数的
大概思路:在 effect 内部我们定义了新的 effectFn 函数,并为其添加了 effectFn.deps 属性,该属性是一个数组,用来存储所有包含当前副作用函数的依赖集合。
设置deps属性:
function effect (fn) {
const effectFn = () => {
// 当其执行时,将其设置为当前激活的副作用函数
activeEffect = effectFn;
fn();
}
// deps用来存储所有与该副作用函数相关联的依赖集合
effectFn.deps = [];
// 执行副作用函数
effectFn();
}
建立依赖集合与effectFn的联系
function track(target , key){
if(!activeEffect){
return target[key];
}
// 根据tartget取来的depsMap,它是一个map类型
let depsMap = bucket.get(target);
// 如果不存在
if(!depsMap){
// 创建一个
bucket.set(target, (depsMap = new Map()));
}
// 根据key取来的deps,它是一个set类型
let deps = depsMap.get(key);
// 如果不存在
if(!deps){
// 创建一个
depsMap.set(key, (deps = new Set()));
}
deps.add(activeEffect); // 添加当前活跃的副作用函数
// 存储存在联系的依赖集合,建立起依赖集合和effectFn的关系
deps.add(activeEffect);
// deps就是一个与当前副作用函数存在联系的依赖集合
// 把它存在activeEffect里面的deps中
activeEffect.deps.push(deps);
}
开始一个个删除联系
function effect (fn) {
const effectFn = () => {
// 调用clearup函数完成清除工作
clearUp(effectFn);
// 当其执行时,将其设置为当前激活的副作用函数
activeEffect = effectFn;
fn();
}
// deps用来存储所有与该副作用函数相关联的依赖集合
effectFn.deps = [];
// 执行副作用函数
effectFn();
}
// 清除函数
function clearUp (effectFn){
// 遍历然后进行删除
for(let i = 0 ; i < effectFn.deps.length ; i++){
const deps = effectFn.deps[i];
// 移除
deps.delete(effectFn);
}
// 最后重置effectFn.deps数组
eff.deps.length = 0;
}
我们的清除遗留副作用函数就大功告成啦!但是为什么一直死循环!!?
问题在trigger函数当中
// 触发变化
function trigger(target , key , newVal){
const depsMap = bucket.get(target);
if(!depsMap){
return;
}
const effects = depsMap.get(key);
effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
}
在这个函数中,我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环。
如何解决?新构造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦
// 触发变化
function trigger(target, key, newVal) {
const depsMap = bucket.get(target);
if (!depsMap) {
return;
}
const effects = depsMap.get(key);
const effectsToRun = new Set(effects);
effectsToRun.forEach(effectFn => effectFn());
// effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
}
造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦
// 触发变化
function trigger(target, key, newVal) {
const depsMap = bucket.get(target);
if (!depsMap) {
return;
}
const effects = depsMap.get(key);
const effectsToRun = new Set(effects);
effectsToRun.forEach(effectFn => effectFn());
// effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
}
总结:解决分支问题,主要是clearUp函数在副作用函数执行前清除旧的依赖关系,确保只有当前实际使用的属性变化时才触发更新,值得注意的是我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环,我们要构造一个集合并遍历他