3-实现effect的runner && scheduler

246 阅读2分钟

runner

effect.spec.ts

  /** runner
   * 1. effect会返回一个function,是它本身
   * 2. function 会返回effect return的数据
   */
  it("return runner when call effect", () => {
    /** effect(fn) -> function(runner) -> fn -> return
     * effect会返回一个function,是它本身
     * function会返回effect return的数据
     */
    let foo = 10;
    const runner = effect(() => {
      foo++;
      return "return-effect";
    });
    expect(foo).toBe(11);
    // 是否返回了本身
    const r = runner();
    expect(foo).toBe(12);
    // 是否返回了effect的return数据
    expect(r).toBe("return-effect");
  });

effect.ts

// 当前活跃的effect实例
let activeEffect;
class Effect {
  private _fn: any;

  constructor(fn) {
    this._fn = fn;
  }

  // 执行effect接收的fn
  run() {
    activeEffect = this;
    // return 执行结果 
    return this._fn();
  }
}

// 收集依赖
const targetMap = new Map(); // 所有的依赖,触发依赖的时候会从这里面取
export function track(target, key) {
  let depMap = targetMap.get(target);
  if (!depMap) {
    depMap = new Map();
    targetMap.set(target, depMap);
  }
  let dep = depMap.get(key);
  if (!dep) {
    dep = new Set();
    depMap.set(key, dep);
  }
  dep.add(activeEffect);
}

// 触发依赖
export function trigger(target, key) {
  let depMap = targetMap.get(target);
  let dep = depMap.get(key);
  for (const effect of dep) {
    effect.run();
  }
}

export function effect(fn) {
  const _effect = new Effect(fn);
  _effect.run();

  // 实现runner
  const runner = _effect.run.bind(_effect);
  return runner;
}

scheduler

effect.spec.ts

  /** 实现scheduler
   * 1.effect接收一个option,里面有一个scheduler
   * 2.存在scheduler,优先调用scheduler,否则调用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++;
    // 验证了scheduler被调用了一次
    expect(scheduler).toHaveBeenCalledTimes(1);
    // 有scheduler,优先调用了scheduler,不会走fn
    // should not run yet
    expect(dummy).toBe(1);
    // manually run
    run();
    // should have run
    expect(dummy).toBe(2);
  });

effect.ts

/*
 * @Author: Lin zefan
 * @Date: 2022-03-15 13:11:07
 * @LastEditTime: 2022-03-16 16:37:01
 * @LastEditors: Lin zefan
 * @Description:
 * @FilePath: \mini-vue3\src\reactivity\effect.ts
 *
 */

// 当前活跃的effect实例
let activeEffect;
class Effect {
  private _fn: any;
  scheduler: any;

  constructor(fn) {
    this._fn = fn;
  }

  // 执行effect接收的fn
  run() {
    activeEffect = this;
    // return 执行结果
    return this._fn();
  }
}

// 收集依赖
const targetMap = new Map(); // 所有的依赖,触发依赖的时候会从这里面取
export function track(target, key) {
  let depMap = targetMap.get(target);
  if (!depMap) {
    depMap = new Map();
    targetMap.set(target, depMap);
  }
  let dep = depMap.get(key);
  if (!dep) {
    dep = new Set();
    depMap.set(key, dep);
  }
  dep.add(activeEffect);
}

// 触发依赖
export function trigger(target, key) {
  let depMap = targetMap.get(target);
  let dep = depMap.get(key);
  for (const effect of dep) {
    if (effect.scheduler) {
      effect.scheduler();
      return;
    }
    effect.run();
  }
}

export function effect(fn, option: any = {}) {
  const _effect = new Effect(fn);
  // 实现scheduler
  _effect.scheduler = option.scheduler;
  // 初始化执行
  _effect.run();
  // 实现runner
  const runner = _effect.run.bind(_effect);
  return runner;
}