返回

揭开Vue3的幕后秘密:200行代码实现mini版reactive + effect

前端

揭秘 Vue3:200行代码中的响应式世界

探索 Reactive 的魔力:数据响应性的关键

在 Vue3 中,Reactive 扮演着数据响应性的魔法师角色。它将普通的数据对象变身为响应式对象,使它们能够在数据发生变化时及时通知视图进行更新。

Reactive 巧妙地对数据对象进行递归遍历,为每个属性套上了一层 getter 和 setter 函数的保护罩。当我们访问数据对象的属性时,getter 函数便会被触发,而当我们修改属性时,setter 函数则会闪亮登场。

Effect:视图的忠实守护者

Effect 函数宛若视图的忠实守护者,时刻监听数据对象的变动,一旦数据发生改变,它便迅速通知视图进行更新。

在创建 Effect 函数时,我们会传入一个回调函数。每当数据对象发生改变时,这个回调函数便会跳出来执行,确保视图与最新数据保持同步。

200行代码的惊人创举

现在,让我们揭开 200 行代码是如何缔造出 Reactive 和 Effect 的传奇。

// Reactive
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      // 触发 getter 函数
      const value = Reflect.get(target, key);
      // 如果是对象,递归调用 reactive
      if (isObject(value)) {
        return reactive(value);
      }
      // 收集依赖
      track(target, key);
      return value;
    },
    set(target, key, newValue) {
      // 触发 setter 函数
      const oldValue = Reflect.get(target, key);
      if (newValue !== oldValue) {
        // 设置新值
        Reflect.set(target, key, newValue);
        // 触发 effect 函数
        trigger(target, key);
      }
      return true;
    }
  });
}

// Effect
function effect(fn) {
  const effectObj = {
    fn,
    deps: new Set()
  };
  // 执行 effect 函数
  effectObj.fn();
  // 将 effect 对象添加到 effect 队列
  activeEffect = effectObj;
  return effectObj;
}

// 依赖收集
function track(target, key) {
  if (activeEffect) {
    // 将 target 和 key 添加到 effect 对象的 deps 集合中
    activeEffect.deps.add(target);
  }
}

// 触发 effect 函数
function trigger(target, key) {
  // 从 effect 队列中找到依赖了 target 和 key 的 effect 对象
  const effects = effectQueue.filter(effectObj => effectObj.deps.has(target));
  // 调用 effect 对象的回调函数
  effects.forEach(effectObj => effectObj.fn());
}

常见问题解答

1. Reactive 和 Effect 的关系是什么?

Reactive 将数据对象变为响应式,而 Effect 监听数据对象的改变并更新视图。

2. 为什么 Reactive 要使用 Proxy?

Proxy 提供了在不修改原始对象的情况下劫持属性访问和修改操作的能力。

3. Effect 如何知道哪些数据对象发生了改变?

Reactive 通过收集依赖关系来跟踪访问和修改数据对象。当数据对象发生改变时,Reactive 会触发 Effect 函数。

4. Effect 的执行时机是什么?

Effect 在以下情况下执行:

  • 创建 Effect 时
  • 响应式数据发生改变时

5. 我可以在 Effect 中执行异步操作吗?

是的,你可以在 Effect 中执行异步操作。