一、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)
...
}