深入理解 Vue 3 响应式系统原理:Proxy、Track 与 Trigger 的协奏曲

深入理解 Vue 3 响应式系统原理:Proxy、Track 与 Trigger 的协奏曲

Vue 3 的响应式系统从 Vue 2 的 Object.defineProperty 升级为基于 Proxy 的全新实现,带来了更强的性能和更高的灵活性。本文将深入剖析其底层原理,解构响应式的关键组成部分,并辅以代码和图示,带你看懂 Vue 3 的响应式魔法。


一、引言:从 Vue 2 到 Vue 3 的响应式变革

Vue 2 使用 Object.defineProperty 拦截对象属性读写,但存在如下限制:

  • 不能监听数组索引和 length 的变化;
  • 不能动态添加属性;
  • 深层嵌套对象递归成本高。

Vue 3 使用 Proxy 解决了这些痛点,实现了更强大、灵活的响应式系统。


二、Vue 3 响应式系统架构图

graph TD A[reactive包裹的对象] --> B[Proxy 对象] B --> C[getter -> track 收集依赖] B --> D[setter -> trigger 派发更新] C --> E[activeEffect 注册副作用] D --> F[effect 重新执行 -> 更新视图]

三、Proxy 是如何劫持对象的?Reflect 有何作用?

Vue 3 利用 Proxy 对原始对象进行拦截,监听属性访问和修改操作:

ts 复制代码
const handler = {
  get(target, key, receiver) {
    // 依赖收集
    track(target, key);
    const result = Reflect.get(target, key, receiver);
    return typeof result === 'object' ? reactive(result) : result;
  },
  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;
  }
};

function reactive(target) {
  return new Proxy(target, handler);
}

Proxy 拦截职责:

  • get:用于依赖收集(track)
  • set:用于触发依赖(trigger)

Reflect 的作用:

  • 提供更规范的原始操作执行方式
  • 返回布尔值表示操作是否成功
  • 避免直接访问原对象,支持继承关系和代理转发

四、track 与 trigger:响应式的灵魂

Vue 响应式系统核心在于:依赖收集(track)依赖触发(trigger)

1. 依赖收集 track

ts 复制代码
let activeEffect = null;

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);
}

每次读取属性时,当前副作用函数(effect)会作为依赖被存储在 targetMap 中,数据结构如下:

ts 复制代码
// WeakMap<target, Map<key, Set<effects>>>
targetMap = {
  obj: {
    name: Set(effect1, effect2),
    age: Set(effect3)
  }
}

2. 依赖触发 trigger

ts 复制代码
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const deps = depsMap.get(key);
  if (deps) {
    deps.forEach(effect => effect());
  }
}

当属性变化时,所有依赖这个属性的 effect 函数将被重新执行。


五、effect 是什么?响应式如何与副作用函数关联?

effect 是用来注册副作用函数的机制。它将副作用函数与响应式数据绑定起来:

ts 复制代码
function effect(fn) {
  const run = () => {
    activeEffect = run;
    fn();
    activeEffect = null;
  };
  run();
}

例如:

ts 复制代码
const state = reactive({ count: 0 });

effect(() => {
  console.log(`count changed: ${state.count}`);
});

上述代码中,state.count 被读取,因此 console.log 所在的 effect 被收集。当 count 改变时,effect 会重新执行,自动更新输出。


六、如何避免重复收集?如何处理嵌套对象?

1. 避免重复收集

使用 Set 去重,保证同一个 effect 只被收集一次:

ts 复制代码
deps.add(activeEffect); // Set 会自动去重

2. 嵌套对象自动递归响应式

get 中递归调用 reactive()

ts 复制代码
const result = Reflect.get(target, key, receiver);
return typeof result === 'object' && result !== null
  ? reactive(result)
  : result;

这确保即使嵌套对象尚未访问,也能在访问时被转为响应式。

例如:

ts 复制代码
const state = reactive({
  user: {
    name: 'Heart',
    address: {
      city: 'guangzhou'
    }
  }
});

只有当你访问 state.user.address.city 时,对应层级才会被递归地变成 Proxy。


七、小结:Vue 响应式的设计与优势

特性 Vue 2 Vue 3
实现方式 Object.defineProperty Proxy
动态属性 不支持 支持
数组监听 局限较多 完善支持
嵌套对象处理 初始化递归 访问时递归,懒代理
性能 响应式递归开销大 更加高效、灵活

Vue 3 的响应式机制不仅解决了 Vue 2 的痛点,还借助现代 JavaScript 特性构建了一个轻量而强大的响应式系统,是前端响应式范式演进的重要一环。


💡 延伸阅读

相关推荐
张鑫旭5 分钟前
AI时代2025年下半年学的这些Web前端特性有没有用?
前端·ai编程
pinkQQx6 分钟前
H5唤醒APP技术方案入门级介绍
前端
Lefan9 分钟前
UniApp 隐私合规神器!一键搞定应用市场审核难题 - lf-auth 隐私合规助手
前端
Null15510 分钟前
浏览器唤起桌面端应用(进阶篇)
前端·浏览器
Jing_Rainbow12 分钟前
【Vue-2/Lesson62(2025-12-10)】模块化与 Node.js HTTP 服务器开发详解🧩
前端·vue.js·node.js
风度前端1 小时前
用了都说好的 uniapp 路由框架
前端
冴羽1 小时前
2026 年 Web 前端开发的 8 个趋势!
前端·javascript·vue.js
码银1 小时前
ruoyi的前端(vue)新增的时候给字典设置默认值 但不能正常
前端
凌览2 小时前
别再死磕 Nginx!http-proxy-middleware 低配置起飞
前端·后端
EndingCoder2 小时前
类的继承和多态
linux·运维·前端·javascript·ubuntu·typescript