vue3源码之effect的stop功能和onStop的实现

151 阅读1分钟

stop的功能:当我们更新响应式值时会触发响应式,更新对应的值,如果不想触发更新可以使用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);
  obj.prop = 3;
  expect(dummy).toBe(2);
  // stopped effect should still be manually callable
  runner();
  expect(dummy).toBe(3);

js源码:

  class ReactiveEffect{
    private _fn:any;
    deps = [];
    active = true;
    OnStop?: () => void;
    public scheduler:Function | undefined
    constructor(fn,scheduler?:Function){
      this._fn = fn;
      this.scheduler = scheduler;
    }
    run(){
      activeEffect = this;
      return this._fn();
    }
    stop(){
    if(this.active){
      cleanupEffect(this);
      if(this.onStop){
        this.onStop();
      }
      this.active = false;
      
    } 
  }
  const targetMap = new Map();
  export function track(target, key){
    let depsMap = targetMap.get(target);
    if(!depsMap){
      depsMap = new Map(); 
      targetMap.set(target, depsMap);
     }
     let dep = depsMap.get(key);
     if(!dep){
       dep = new Set();
       depsMap.set(key, dep);
      }
      if(!activeEffect) return;
      dep.add(activeEffect);
      // 反向收集deps
      activeEffect.deps.push(dep);
  }
  
  export function trigger(target, key){
    let depsMap = targetMap.get(target);
    let dep = depsMap.get(key);
    for(const effect of dep){
    if(effect.scheduler){
      effect.scheduler();
     } else {
      effect.run();
     }
   }
   export function cleanupEffect(effect:any){
     effect.deps.forEach(dep => {
       dep.delete(effect);
     }
   }
   let activeEffect;
   export function effect(fn, options:any=[]){
     const _effect = new reactiveEffect(fn, options.scheduler);
     // 接收onStop参数
     // _effect.onStop = options.onStop;
     // 这样写更优雅
     Object.assign(_effect, options);
     _effect.run();
     const runner = _effect.run.bind(_effect);
     // 先挂载下effect
     runner.effect = _effect;
     return runner;
   }
   export function stop(runner){
     runner.effect.stop();
   }