vue 3 响应式系统底层基于 proxy 实现,通过 get 拦截器自动 track 收集依赖、set 拦截器 trigger 精准通知更新,支持新增属性、数组索引等动态操作,且依赖存储优化为 target+key→set 的两级 map 结构。

Vue.js 的响应式系统底层对 getter 与 setter 的高效包装,本质不是“手动写一堆 get/set 函数”,而是通过语言原生能力(Object.defineProperty 或 Proxy)自动拦截访问行为,并在拦截点中注入依赖追踪逻辑。这种包装轻量、透明,且与开发者代码解耦。
Vue 2 中的 getter/setter 包装方式
Vue 2 使用 Object.defineProperty 对 data 中每个属性逐个重定义:
- 每个属性被包裹进一个闭包作用域,内部维护独立的依赖列表(Dep 实例)
-
get阶段:若当前存在活跃的 Watcher(如组件渲染函数),就将其加入该属性的依赖池 -
set阶段:不仅赋新值,还调用dep.notify()通知所有 Watcher 更新 - 数组特殊处理:重写
push、pop等 7 个原型方法,在内部手动触发更新
这种方式的问题在于:
- 无法监听对象新增/删除属性(需
Vue.set) - 无法监听数组索引直接赋值(如
arr[0] = x) - 初始化时必须遍历全部属性,深层嵌套对象需递归
defineReactive
Vue 3 中的 getter/setter 逻辑升级
Vue 3 放弃逐属性定义,改用 Proxy 代理整个对象:
-
get(target, key)拦截读取:自动调用track(target, key)收集依赖 -
set(target, key, value)拦截赋值:先赋值,再调用trigger(target, key)触发更新 - 支持任意属性操作:新增、删除、
in判断、hasOwnProperty、Reflect等全部可拦截 - 基础类型用
ref封装:内部仍是get value()/set value(),但统一走track/trigger流程
关键优化点:
- 依赖存储结构从“每个属性一个 Dep”变为“target + key → Set”的两级 Map(targetMap → keyToDepMap → deps)
-
track和trigger是纯函数,不耦合 Watcher 类,支持任意副作用(computed、watch、组件 render)
如何保证包装不拖慢运行速度?
- 拦截只发生在响应式数据被真正访问或修改时(惰性)
-
track仅在有活跃 effect 时才收集(避免无效记录) -
trigger只通知已注册的依赖,跳过未使用该字段的组件 -
Proxy的 V8 优化比大量defineProperty更快,尤其在动态属性场景
例如:
const state = reactive({ count: 0 })
// 第一次读取 count 时,track 被调用,记录当前 effect
console.log(state.count)
// 修改时 trigger 执行,只通知订阅了 count 的 effect
state.count++
这种包装不是语法糖,而是运行时数据流调度的核心枢纽。它让数据变化与视图更新之间形成可预测、可追踪、可中断的链路。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











