Appearance
js
import {ref, effect} from '../dist/reactivity.esm.js'
const count = ref(0)
effect(() => {
effect(() => {
console.log('count.value1 ==> ', count.value)
})
console.log('count.value2 ==> ', count.value)
})
setTimeout(() => {
count.value = 1
}, 1000)
改一下 case,当遇到嵌套的 effect 时,结果会如何?
在这种情况下,实际的输出结果是
js
console.log('count.value1 ==> ', count.value) // 0
console.log('count.value2 ==> ', count.value) // 0
console.log('count.value1 ==> ', count.value) // 1
比如说这种场景下就会遇到 嵌套的问题
ts
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
effect(() => {
console.log('doubleCount.value ==> ', doubleCount.value)
})
问题原因
mermaid
graph TD
A[外层 effect 开始执行] --> B[ReactiveEffect.run 设置 activeSub=外层 effect]
B --> C[内层 effect 开始执行]
C --> D[ReactiveEffect.run 设置 activeSub=内层 effect]
D --> E[触发 getter 建立依赖]
E --> F[依赖建立后清理 activeSub = undefined]
F --> G[外层继续执行 console.log(count.value)]
G --> H[触发 getter]
H --> I[因为 activeSub 是undefined 不能正确建立关联关系]
解决方式
问题原因发现了之后,解决方式也就很简单了
- 在执行内层 effect 之前,先把外层 effect 保存起来
- 内层 effect 执行完毕之后,再把外层 effect 重新赋值回去
所以现在的代码是这样了
ts
export let activeSub: ReactiveEffect | undefined
export class ReactiveEffect {
constructor(public fn: Function) {
}
run(): any {
const prevSub = activeSub // 将当前的 effect 保存下来, 防止嵌套时丢失
activeSub = this
try {
return this.fn()
} finally {
// 执行完成的时候, 恢复之前的 effect
activeSub = prevSub
}
}
}
export function effect(fn) {
const e = new ReactiveEffect(fn)
e.run()
}