mini-vue3:实现effect的stop和onStop功能

92 阅读2分钟

一、stop实现单测

// 在effct.spec.ts中继续添加effect的stop单测
it('stop', () => {
    let dummy
    const obj = reactive({ prop: 1 })
    const runner = effect(() => {
      dummy = obj.prop
    })
    obj.prop = 2
    expect(dummy).toBe(2)
    stop(runner)
    // stop调用后,再修改prop的值,值不会改变
    obj.prop = 3
    expect(dummy).toBe(2)

    // stopped effect should still be manually callable,再次执行runner(),数据更新
    runner()
    expect(dummy).toBe(3)
 })

二、stop代码实现

stop 接收一个 runner,调用 stop 后,runner 依赖的数据发生变化不再执行 effect 函数传入的fn,effect反向收集deps deps是一个与当前副作用函数存在联系的依赖集合,添加到activeEffect.deps中,完成了对依赖集合的收集,在调用stop时,删除掉依赖集合的每一项

// effect.ts中继续实现
class ReactiveEffect {
  deps = []; // 反向收集 dep
  active = true // 做开关,判断是否执行过stop 防止多次调用出现性能问题
  ...
  stop() {
    if (this.active) {
      cleanupEffect(this)
      this.active = false
    }
  }
}
function cleanupEffect(effect) {
  // 删除 effect 存储的 dep
  effect.deps.forEach((dep: any) => {
    dep.delete(effect)
  })
}
export function track(target, key) {
    ... 
    if (activeEffect) { dep.add(activeEffect)
    // effect反向收集deps deps是一个与当前副作用函数存在联系的依赖集合,添加到activeEffect.deps中,完成了对依赖集合的收集 
    activeEffect.deps.push(deps)
} }

let activeEffect;
// effect 函数用于注册副作用函数
export function effect(fn, options: any = {}) {
    ... 
    const runner: any = _effect.run.bind(_effect) // 将_effect实例作为runner的属性挂载到runner上,这样我们就可以通过runner得到effect实例
    runner.effect = _effect return runner 
}

// 导出 stop 函数
export function stop(runner){
    runner.effect.stop()
}

三、onStop单测

onStop是调用 stop 后的回调函数,是第二个参数,调用 stop 后会执行 onStop ,onStop 和 scheduler 同为 option 的属性,因此在effect 的 option 中进行接收

 it('onStop', () => {
    // 调用 stop 后的回调函数
    const obj = reactive({ foo: 1 })
    const onStop = jest.fn()
    let dummy
    const runner = effect(() => {
      dummy = obj.foo
    }, {
      onStop,
    })

    stop(runner)
    expect(onStop).toBeCalledTimes(1)

  })
})

四、实现 onStop

// effect.ts 
class ReactiveEffect {
  ...
  onStop?: () => void
  ...
  stop() {
    if (this.active) {
      ...
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

export function effect(fn, options: any = {}) {
  ...
  // 将options上的属性,添加到_effect上
  Object.assign(_effect, options)
  ...
}