Skip to content
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 不能正确建立关联关系]

解决方式

问题原因发现了之后,解决方式也就很简单了

  1. 在执行内层 effect 之前,先把外层 effect 保存起来
  2. 内层 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()
}

最后更新时间: