Vue3响应式原理与实现详解
字数 947 2025-11-10 02:41:35

Vue3响应式原理与实现详解

1. 响应式系统的核心目标

Vue3的响应式系统通过ProxyReflect实现,其核心目标是:当数据变化时,自动触发依赖该数据的副作用函数(如视图更新、计算属性重新计算等)。


2. 核心概念:副作用函数(Effect)

副作用函数指执行时会直接或间接读取响应式数据的函数。例如:

const data = { count: 1 };
function effect() { 
  document.body.innerText = data.count; // 读取data.count
}

理想情况下,当data.count变化时,应重新执行effect函数。


3. 依赖收集与触发更新

3.1 依赖收集的原理

  1. 存储结构:使用WeakMap(target → Map(key → Set(effect)))三层结构存储依赖关系。

    • WeakMap的键是响应式对象(target),值是一个Map
    • Map的键是对象的属性(key),值是一个Set,存储所有依赖该属性的副作用函数。
  2. 触发时机

    • 读取数据时(get):将当前执行的副作用函数存入对应属性的依赖集合中。
    • 修改数据时(set):从依赖集合中取出所有副作用函数并执行。

3.2 实现步骤

步骤1:创建临时变量存储当前副作用函数

let activeEffect = null; // 当前正在执行的副作用函数

步骤2:定义依赖收集函数(track)

const targetMap = new WeakMap(); // 全局依赖存储
function track(target, key) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  let deps = depsMap.get(key);
  if (!deps) {
    deps = new Set();
    depsMap.set(key, deps);
  }
  deps.add(activeEffect); // 将当前副作用函数加入依赖集合
}

步骤3:定义触发更新函数(trigger)

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const deps = depsMap.get(key);
  if (deps) {
    deps.forEach(effect => effect()); // 重新执行所有依赖函数
  }
}

4. 响应式对象的实现

通过Proxy代理对象的get和set操作:

function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key); // 读取时收集依赖
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      if (oldValue !== value) { // 避免重复触发
        trigger(target, key); // 设置时触发更新
      }
      return result;
    }
  });
}

5. 副作用函数的注册

定义effect函数,用于注册副作用函数并立即执行一次:

function effect(fn) {
  activeEffect = fn; // 设置当前活跃的副作用函数
  fn(); // 执行函数,触发get操作以收集依赖
  activeEffect = null; // 重置
}

6. 完整示例测试

const data = reactive({ count: 1 });
effect(() => {
  console.log("count变化了:", data.count);
});
data.count = 2; // 输出:"count变化了: 2"

7. 进阶优化:分支切换与cleanup

若副作用函数中存在条件分支(如data.flag ? data.a : data.b),当flag变化时需清理不再依赖的属性。Vue3通过给副作用函数绑定deps数组,并在每次执行前清理旧依赖来实现。


8. 总结

Vue3响应式系统的核心流程:

  1. 依赖收集:通过Proxy的get拦截,将副作用函数存入对应属性的依赖集合。
  2. 触发更新:通过Proxy的set拦截,执行依赖集合中的所有副作用函数。
  3. 副作用管理:通过effect函数注册副作用,并处理分支切换的依赖清理。

与Vue2的Object.defineProperty相比,Proxy能直接代理对象(而非属性),无需递归初始化,且支持动态新增属性的响应式。

Vue3响应式原理与实现详解 1. 响应式系统的核心目标 Vue3的响应式系统通过 Proxy 和 Reflect 实现,其核心目标是:当数据变化时,自动触发依赖该数据的副作用函数(如视图更新、计算属性重新计算等)。 2. 核心概念:副作用函数(Effect) 副作用函数指执行时会直接或间接读取响应式数据的函数。例如: 理想情况下,当 data.count 变化时,应重新执行 effect 函数。 3. 依赖收集与触发更新 3.1 依赖收集的原理 存储结构 :使用 WeakMap(target → Map(key → Set(effect))) 三层结构存储依赖关系。 WeakMap 的键是响应式对象( target ),值是一个 Map 。 Map 的键是对象的属性( key ),值是一个 Set ,存储所有依赖该属性的副作用函数。 触发时机 : 读取数据时(get) :将当前执行的副作用函数存入对应属性的依赖集合中。 修改数据时(set) :从依赖集合中取出所有副作用函数并执行。 3.2 实现步骤 步骤1:创建临时变量存储当前副作用函数 步骤2:定义依赖收集函数(track) 步骤3:定义触发更新函数(trigger) 4. 响应式对象的实现 通过Proxy代理对象的get和set操作: 5. 副作用函数的注册 定义 effect 函数,用于注册副作用函数并立即执行一次: 6. 完整示例测试 7. 进阶优化:分支切换与cleanup 若副作用函数中存在条件分支(如 data.flag ? data.a : data.b ),当 flag 变化时需清理不再依赖的属性。Vue3通过给副作用函数绑定 deps 数组,并在每次执行前清理旧依赖来实现。 8. 总结 Vue3响应式系统的核心流程: 依赖收集 :通过Proxy的get拦截,将副作用函数存入对应属性的依赖集合。 触发更新 :通过Proxy的set拦截,执行依赖集合中的所有副作用函数。 副作用管理 :通过 effect 函数注册副作用,并处理分支切换的依赖清理。 与Vue2的 Object.defineProperty 相比,Proxy能直接代理对象(而非属性),无需递归初始化,且支持动态新增属性的响应式。