精读《Vuejs设计与实现》(17)之响应系统7

92 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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是很粗放,是没有优化,也没有优化的想法的。但这一章就提出了第一个可以优化点:选择分支时,可以优化副作用函数的调用次数。

这一章写的真的好,推荐大家仔细阅读,我这两天就只看了这一章