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 的性能优化奠定了基础。

相关推荐
小小小小宇7 小时前
Vue `import` 为什么可以异步加载
前端
WMYeah7 小时前
【无标题】
前端·rust·抽奖程序·跨平台抽奖程序
Unbelievabletobe7 小时前
免费外汇api的响应时间在不同时段下的波动分析
大数据·开发语言·前端·python
大哥,带带弟弟7 小时前
Grafana 前端嵌入与 JWT 鉴权实战
前端·grafana
小小小小宇7 小时前
前端 V8 引擎垃圾回收机制与内存问题排查
前端
前端老石人7 小时前
CSS 值定义语法
前端·css
sheeta19988 小时前
Vue 前端基础笔记
前端·vue.js·笔记
小小小小宇8 小时前
GitLab + GitLab Runner + Qiankun 微前端 + Nginx + Node 中间件 前端开发机从零搭建 CI/CD 全流程
前端
前端那点事8 小时前
别再写垃圾组件!Vue3 如何设计「真正可复用」的高质量通用组件
前端·vue.js
卷帘依旧8 小时前
JavaScript 中的 Symbol
前端·javascript