Vue3 响应式革命

Vue2 验证了「响应式驱动视图」的威力,却在数组索引、属性增删等场景留下无法追踪的死角。Vue3 并未缝缝补补,而是把整座大厦的地基------数据拦截机制------彻底替换为 Proxy。本文沿着「拦截 → 创建 → 收集」三条链路,带你读懂这一次底层跃迁的全部细节。

一、拦截:从点到面的语义升级

1.Vue2 的「定点拦截」

Object.defineProperty 只能劫持已存在的属性。当业务代码 obj.newKey = 1 时,这条新增路径对依赖系统完全不可见,于是官方只能额外暴露 Vue.set / vm.$set 作为补丁。

2.Vue3 的「整面拦截」

Proxy 把「对象」视为一个整体,任何对属性的 读取、写入、删除、遍历 乃至 原型链读取 都能被捕获。

ts 复制代码
const p = new Proxy(obj, {
  get(target, key, receiver) { /* 读 */ },
  set(target, key, value, receiver) { /* 写 */ },
  deleteProperty(target, key) { /* 删 */ }
})

新增 key 不再逃逸,数组索引与 length 的变化自然落入监听范围,彻底告别 $set

二、创建:ref 与 reactive 的分工

虽然 Proxy 能力更强,但「原始值」无法被代理。Vue3 用 RefImpl 与 ReactiveImpl 两套实现互补:

  • ref 负责原始值(Number、String、Boolean)------内部用 RefImpl 包裹,读/写时触发自定义 getter/setter;当值是对象时再递归交给 reactive
  • reactive 负责对象/数组------直接返回 Proxy,拦截全部操作。

源码片段(精简):

ts 复制代码
class RefImpl<T> {
  get value() {
    track(this, 'value')   // 收集
    return this._value
  }
  set value(newVal) {
    this._value = toReactive(newVal)
    trigger(this, 'value') // 派发
  }
}

function reactive(target: object) {
  return createReactiveObject(target, mutableHandlers)
}

toReactive 判断传入值是否为对象,是则继续包 Proxy,否则原样返回,形成一条「层层代理,按需终止」的链。

三、收集:从 Watcher 树到副作用图

1.Vue2 的 Watcher + Dep

每个响应式属性拥有一个 Dep,Dep 里存着若干 Watcher(通常是一个组件渲染函数)。属性变化 → 通知所有 Watcher → 组件级重渲染。

粒度:组件级别。

2.Vue3 的副作用图

Vue3 不再关心「是哪个组件」,而是关心「哪个副作用函数」。数据结构是一张 WeakMap → Map → Set 的三级表:

  • 第一级 WeakMap:键是响应式对象,值是第二级 Map;
  • 第二级 Map:键是属性名,值是第三级 Set;
  • 第三级 Set:存储所有依赖该属性的 effect 函数。

当属性值改变时,只触发 精确到函数 的重新执行,粒度从组件级降到函数级,更新范围更小,性能更高。

四、工程实践的迁移

  • Vue2 项目:若观察到大量 this.$setVue.set,说明已踩中响应式盲区,升级 Vue3 可一次性消除。
  • 新 Vue3 项目:优先使用 reactive 管理对象,ref 管理原始值;避免把 reactive 包进 ref,防止双重代理带来的额外开销。
  • 性能调优:借助 markRawshallowReactive 把大列表、第三方库实例标记为非响应式,减少追踪压力。

结语

Vue3 的响应式不是「打补丁」,而是「换引擎」。Proxy 让「增删改查」全部可追踪,WeakMap 让依赖收集更精准。这次底层跃迁,彻底解决了 Vue2 的响应式难题,也为 Vue3 的性能优化奠定了基础。

相关推荐
thatway19891 小时前
闲聊-关于AI终结者的警醒
前端
努力的小郑2 小时前
突发!Claude Code 51万行源码全网裸奔:一场史诗级“开源”事故,国内大厂笑麻了
前端·后端·ai编程
七度黑光2 小时前
用 openclaw 给故障复盘打分:质量审核自动化实践
运维·服务器·前端·数据库·自动化
HashTang2 小时前
Claude Code 源码中 REPL.tsx 深度解析:一个 5005 行 React 组件的架构启示
前端·后端·ai编程
wendycwb3 小时前
前端城市地址根据最后一级倒推,获取各层级id的方法
前端·vue.js·typescript
Σίσυφος19003 小时前
C++ 多肽经典面试题
开发语言·c++·面试
终端鹿3 小时前
Vue3 模板引用 (ref):操作 DOM 与子组件实例 从入门到精通
前端·javascript·vue.js
软件测试媛3 小时前
2026软件测试面试题大全(含答案+文档)
功能测试·测试工具·面试·ai软件测试
千寻girling3 小时前
不知道 Java 全栈 + AI 编程有没有搞头 ?
前端·人工智能·后端
小码哥_常4 小时前
Android开发:精准捕获应用的前后台行踪
前端