持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
4.4 分支切换与cleanup
我们继续刨析cleanup。前文中,我们修改了track函数,现在我们在看一下trigger
const trigger = (target,key)=>{
const _targetMap = targetsMap.get(target)
if(!_targetMap){return }
const deps = effectStore.get(key)
deps && deps.forEach(fn=>fn())
}
我们之前就说了,每次执行副作用函数之前,我们都会调用cleanup函数,实际上就是从effects集合中将当前执行的那个副作用函数删除,然后再次添加回去,但此时你的trigger有可能还在遍历,所以就会导致你的遍历一直结束不了,造成死循环。 就好比这样
const set = new Set([1])
set.forEach(()=>{
set.delete(1)
set.add(1)
console.log('on the forEach')
})
我们不断的删除插入,forEach就会不停的循环,这个问题其实也不算是bug。我的理解是,对于数组,删除再插入,数组的length变化了一次,那么forEach就会觉得数组内的元素是新元素,从而继续遍历。forEach并不会对比每次便利的值是否一样,也无需对比。
解决的办法需要一点发散思维,就是新建一个Set去遍历,可以理解为深拷贝
const set = new Set([1])
const set2 = new Set([1])
set2.forEach(()=>{
set.delete(1)
set.add(1)
console.log('on the forEach')
})
这样就不会陷入无限循环了,所以我们的trigger也需要改一下。
const trigger = (target,key)=>{
const depsMap = targetsMap.get(target)
if(!depsMap){return }
const deps = depsMap.get(key)
const sideEffects = new Set(deps)
deps && sideEffects.forEach(fn=>fn())
}
其实这一章需要好好阅读一下,我之所以拆成2章去写,也是表明这一章内容很重要。如果你看前几章,你可以很轻松的去实现一个reactive,但是这个reactive是很粗放,是没有优化,也没有优化的想法的。但这一章就提出了第一个可以优化点:选择分支时,可以优化副作用函数的调用次数。
这一章写的真的好,推荐大家仔细阅读,我这两天就只看了这一章