持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情
4.5嵌套的effect和effect栈
其实effect内部还会嵌套effect,最典型的一个场景就是组件的渲染。 我们现在又这样的一个组件
<template>
<Foo>
<Bar />
</Foo>
</template>
我们把它翻译为副作用函数就相当于
effect(()=>{
render(Foo)
effect(()=>{
render(Bar)
})
})
这说明effect应该被设计成允许嵌套的。但是如果你用我之前的代码跑一下,你会发现一个问题,嵌套最里面哪个副作用函数并不会执行。这之前以为,不可能会在同一时间调用effect2次,但是现在情况是嵌套的,也就是说可能会在同一个函数中对activeEffect赋值2次,这样势必会丢失一次赋值。拿上面的例子来讲,Bar会覆盖掉Foo,这时候Bar的响应式就丢失了,且永远不会再次赋值。这样很明显就时错的。
这时候,我们需要引入一个新的数据结构:栈
栈(stack)是允许在同一端进行插入和删除操作的特殊线性表。 允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。 插入一般称为进栈(PUSH),删除则称为退栈(POP)。 栈也称为先进后出表。
在js中并没有栈这个对象,但是我们可以通过数组去模拟。
我们在全局再新建一个变量effectStack
let activeEffect
const effectStack = []
const effect((fn)=>{
const runEffect=()=>{
cleanup(runEffect) // 清除重复依赖
activeEffect = runEffect
effectStack.unshift(runEffect) //将副作用函数压入栈中
fn()
effectStack.pop() // 执行完成后,出栈
activeEffect = effectStack.at(-1) //将activeEffect对准栈底
}
runEffect.deps=[]
runEffect()
})
我们定义了一个effectStack来模拟栈,activeEffect没有变化,依旧只想当起执行的副作用函数。不同的是,我们会把当前执行的副作用压入栈顶(原书代码中用了push,但我觉得应该是unshift,要不然就不是先进后出了)。这样当副作用发生嵌套时,栈底就是最外层嵌套的。当最内层执行完,会被出栈,而activeEffect则会执行外层的副作用函数。
原书这里应该有点错误,大家看到这里也可以想一想,看是不是出错了