返回

Effect 源码剖析——解读 @vue-reactivity 中的 effect

前端

当我们通过 effect 将副作用函数向响应上下文注册后,副作用函数内访问响应式对象时即会自动收集依赖,并在相应的响应式属性发生变化后,自动触发副作用函数的执行。effect 函数的代码十分少,主要流程是 将副作用函数包裹在一个 try-catch 块中,并在其中调用副作用函数,当副作用函数执行过程中访问响应式对象时,会触发响应式对象的依赖收集,从而将副作用函数收集到响应式对象的依赖列表中。当响应式属性发生变化时,会通知所有依赖于它的副作用函数,从而触发这些副作用函数的执行。

在 effect 函数中,首先会对副作用函数进行一些必要的处理,包括:将副作用函数包裹在一个 try-catch 块中,以便捕获副作用函数执行过程中可能抛出的异常;将副作用函数的执行上下文设置为当前的响应上下文,以便副作用函数能够访问响应式对象。

接下来,effect 函数会调用副作用函数。当副作用函数执行过程中访问响应式对象时,会触发响应式对象的依赖收集,从而将副作用函数收集到响应式对象的依赖列表中。

当响应式属性发生变化时,会通知所有依赖于它的副作用函数,从而触发这些副作用函数的执行。

effect 函数的代码如下:

export function effect(fn, options = {}) {
  const effect = createReactiveEffect(fn, options);
  if (!options.lazy) {
    effect.run();
  }
  const runner = effect.run.bind(effect);
  runner.effect = effect;
  return runner;
}

createReactiveEffect 函数的作用是创建一个响应式副作用函数,该函数会自动收集依赖并会在响应式属性发生变化时触发执行。createReactiveEffect 函数的代码如下:

export function createReactiveEffect(fn, options) {
  const effect = function reactiveEffect() {
    if (!effect.active) {
      return fn();
    }
    try {
      return fn();
    } catch (e) {
      console.error(e, effect.fn);
    }
  };
  effect.deps = [];
  effect.options = options;
  return effect;
}

effect 函数的 run 方法的作用是执行响应式副作用函数。run 函数的代码如下:

effect.run = function() {
  if (!effect.active) {
    return;
  }
  stop(effect);
  const parentEffect = activeEffect;
  activeEffect = effect;
  effect.fn();
  activeEffect = parentEffect;
};

在 effect 函数中,我们使用了 activeEffect 变量来记录当前正在执行的响应式副作用函数。当一个响应式副作用函数开始执行时,我们将 activeEffect 设置为该副作用函数,当该副作用函数执行结束时,我们将 activeEffect 恢复为之前的状态。这样做是为了防止嵌套的响应式副作用函数互相影响。

在 effect 函数中,我们还使用了 deps 数组来存储响应式副作用函数依赖的响应式属性。当一个响应式属性发生变化时,我们将遍历 deps 数组,并调用所有依赖于该属性的响应式副作用函数。

以上就是 effect 函数的源码剖析。通过对 effect 函数源码的分析,我们可以更好地理解响应式系统是如何实现的。