8.实现effect的scheduler功能

124 阅读1分钟

这一小节实现effect的scheduler功能。

编写单元测试

首先写一下单元测试。主要有4点:

  1. 通过effect的第二个参数给定的一个scheduler的fn
  2. effect第一次执行的时候,还会执行fn(effect的第一个参数)
  3. 当响应式对象set、update时不会执行fn(effect的第一个参数),而是执行scheduler
  4. 当执行runner的时候,会再次的执行fn
it("scheduler", () => {
    let dummy;
    let run:any;
    const scheduler = jest.fn(()=> {
      run = runner;
    });
    const obj = reactive({foo:1});
    const runner = effect(
      () => {
        dummy = obj.foo;
      },
      {scheduler}
    );
    expect(scheduler).not.toHaveBeenCalled();
    expect(dummy).toBe(1);
    // should be called on first trigger
    obj.foo++;
    expect(scheduler).toHaveBeenCalledTimes(1);
    // should not run yet
    expect(dummy).toBe(1);
    // manually run
    run();
    // should have run
    expect(dummy).toBe(2);
  })

功能实现

class ReactiveEffect{
  private _fn:any;
  constructor(fn,public scheduler?){
    this._fn = fn;
  }
  run(){
    activeEffect = this;
    return this._fn();
  }
}

const targetMap = new Map();
export function track(target,key){
  // target -> key -> dep
  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)
  }
  dep.add(activeEffect);
}
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();
    }
  }
}

let activeEffect;
export function effect(fn, options: any= {}){
  const scheduler = options.scheduler;
  const _effect = new ReactiveEffect(fn,scheduler);
  _effect.run();
  return _effect.run.bind(_effect)
}

具体修改点可以看下图

image.png

image.png

执行单元测试

功能实现后我们可以执行单元测试:

yarn test effect

可以看到测试通过

image.png