持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
从这一章开始,难度陡增了。我尽量用通俗的语言表达书中的主要内容,示例代码尽量也用我自己的理解,如有错误,欢迎留言
4.4 分支切换与cleanup
什么是分支切换?
我相信大部分人都写过这种代码
const data = reactive({login:false,text:''})
const effectFn = ()=> data.login ? data.text : 'please login'
const text = computed(effectFn)
computed再加上三目运算符很好用,可以作条件的切换.
但是对于我们之前写的reactive那个方法就有问题了。
首先,effectFn这个函数中使用到了,当我们读取data.login和data.text时,都会收集到effectFn.
其次,effectFn中其实只在data.login === true时才有用,当data.false === false时,其实无需追踪data.text.
最后effectFn里其实主要依赖的是data.login,data.text的变化完全不会也不需要引起副作用函数的执行。
但是在我们之前实现的reactive中,读取任意属性都会被添加依赖。这样就容易造成副作用函数的无用执行,执行次数过多。
书中是这样解决的,执行副作用函数时,先把它从所有与之关联的依赖集合中删除。然后执行完再把它添加回去,这样就能保证只执行一次副作用函数
要想将一个副作用函数从所有与之关联的的依赖集合中删除,就需要明确知道那些依赖集合中包含了它。因此我们需要修改副作用函数,给他添加一个属性,用来保存所有包含当前副作用函数的依赖集合。
let activeEffect
const effect = (fn)=>{
const runEffect = ()=>{
//这里需要cleanup
activeEffect = runEffect
fn()
}
runEffect.deps = []
runEffect()
}
那么,这个runEffect.deps是在什么时候使用呢,其实是在track的时候
const track = (target,key)=>{
if (!activeEffect) { return; }
let depsMap = targetsMap.get(target);
if (!depsMap) { targetsMap.set(target, (depsMap = new Map())); }
let dep = depsMap.get(key);
if (!dep) { depsMap.set(key, (dep = new Set())); }
dep.add(activeEffect);
//新增这句
activeEffect.deps.push(dep)
}
这样,我们就收集到了这个副作用函数与之关联的依赖,因此我们就可以写出一个cleanup函数
const cleanup = (effectFn)=>{
for(let i=0;i<effectFn.deps.length;i++){
const deps = effectFn.deps[i]
//这里取到的其实是set
deps.delete(effectFn)
}
//清楚后,重置
effectFn.deps=[]
}
cleanup会遍历副作用中的依赖集合,删除自身,最好重置依赖集合。但是如果你运行这段代码,你会发现一个新的问题,我们下一篇再看。