SOURCE

console 命令行工具 X clear

                    
>
console
// 原始数据
const data = {
  ok: true,
  text: "hello",
  foo: 1,
};

// 副作用函数
let activeEffect;

// effect栈
const effectStack = [];

const effect = (fn, options = {}) => {
  const effectFn = () => {
    cleanup(effectFn);
    activeEffect = effectFn;
    effectStack.push(activeEffect); // 执行前入栈
    fn();
    effectStack.pop(); // 执行后出栈
    activeEffect = effectStack[effectStack.length - 1]; // 确保嵌套使用时activeEffect的指向正确
  };

  effectFn.options = options;
  effectFn.deps = []; // 用与存放那些依赖集合,有effect自己
  effectFn();
};

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];
    deps.delete(effectFn);
  }

  effectFn.deps.length = 0;
}

// effect 存储容器
const bucket = new WeakMap();
// 响应式数据
const obj = new Proxy(data, {
  // 拦截取
  get(target, key) {
    track(target, key);
    return target[key];
  },
  // 拦截改
  set(target, key, newVal) {
    target[key] = newVal;
    trigger(target, key);
  },
});

// track抽取
function track(target, key) {
  if (!activeEffect) return target[key];

  let depsMap = bucket.get(target);
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }

  let deps = depsMap.get(key);
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }

  deps.add(activeEffect);
  activeEffect.deps.push(deps);
}

// trigger抽取
function trigger(target, key) {
  const depsMap = bucket.get(target);
  if (!depsMap) return;
  const effects = depsMap.get(key);

  const effectsToRun = new Set();
  effects &&
    effects.forEach((effectFn) => {
      if (effectFn !== activeEffect) {
        // 避免如自增情况下的无限循环递归
        effectsToRun.add(effectFn);
      }
    });

  effectsToRun.forEach((effectFn) => {
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn);
    } else {
      effectFn();
    }
  });
}

effect(() => {
  console.log("effect run");
  obj.foo++;
  // document.getElementById("test").innerText = obj.ok ? obj.text : "not";
});

setTimeout(() => {
  obj.ok = false;
  obj.text = "ha ha ha !!!";
}, 1000);
<div id="test"></div>