文章目录
- 1 响应式基础:Proxy 与 Reflect
- 1.1 Proxy 代理拦截
- 1.2 Reflect 确保 `this` 指向正确
- 1.2.1 修正 `this` 指向问题
- 1.2.2 统一的操作返回值
- 1.3 与 Vue2 的对比
- 2 依赖收集与触发机制
- 2.1 全局依赖存储结构:WeakMap → Map → Set
- 2.2 依赖收集触发时机
- 2.3 依赖收集核心实现:track 函数
- 2.4 依赖触发:trigger 函数
- 2.5 副作用管理:ReactiveEffect 类
- 2.6 特殊场景处理:ref 的依赖收集
- 2.7 设计亮点
- 3 响应式 API 的封装
- 3.1. `reactive`
- 3.2 `ref`
- 3.3 `reactive` 与 `ref` 核心区别
- 3.4 注意事项
- 4 双向绑定实现(v-model)
- 4.1 原生 DOM 元素的实现原理
- 4.1.1 属性绑定
- 4.1.2 响应式更新
- 4.2 自定义组件的实现原理
- 4.2.1 传统模式(Vue3.4 前)
- 4.2.2 `defineModel` 宏(Vue3.4+)
- 4.3 响应式系统的支撑
- 4.4 性能优化与扩展性
- 5 对比 Vue2 的改进
近期文章:
- Vue3开发常见性能问题知多少
- Vue3组件常见通信方式你了解多少?
- 实现篇:LRU算法的几种实现
- 从底层视角看requestAnimationFrame的性能增强
- Nginx Upstream了解一下
- 实现篇:一文搞懂Promise是如何实现的
- 实现篇:如何手动实现JSON.parse
- 实现篇:如何亲手定制实现JSON.stringify
- 一文搞懂 Markdown 文档规则
Vue3 的双向响应式原理是其核心机制,通过 Proxy 代理与 依赖收集系统 实现数据与视图的自动同步。以下是其核心原理与技术细节的深度解析:
1 响应式基础:Proxy 与 Reflect
Vue3 彻底抛弃了 Vue2 的 Object.defineProperty
,改用 ES6 Proxy 实现数据劫持,解决了 Vue2 无法监听对象属性新增/删除、数组索引修改等问题。
1.1 Proxy 代理拦截
Proxy 可拦截对象的所有操作(如 get
、set
、deleteProperty
等),解决了 Vue2 中 Object.defineProperty
无法监听新增属性和数组索引修改的问题。
Proxy 默认采用惰性代理,仅在访问嵌套对象时递归创建代理,优化了性能。
const handler = {get(target, key, receiver) {track(target, key); // 依赖收集return Reflect.get(target, key, receiver);},set(target, key, value, receiver) {const result = Reflect.set(target, key, value, receiver);trigger(target, key); // 触发更新return result;}
};
const proxyData = new Proxy(data, handler);
1.2 Reflect 确保 this
指向正确
Reflect 的静态方法与 Proxy 的拦截器一一对应,确保操作的一致性和安全性:
1.2.1 修正 this
指向问题
当代理对象包含访问器属性(如 get c() { return this.a + this.b }
)时,若直接通过 target[key]
读取属性,this
会指向原对象而非代理对象,导致后续属性访问无法触发 Proxy 拦截。
解决方案:使用 Reflect.get(target, key, receiver)
,其中 receiver
显式传递代理对象,确保 this
指向正确。
// 错误示例(this指向原对象)get(target, key) { return target[key]; }// 正确示例(通过Reflect修正this)get(target, key, receiver) { return Reflect.get(target, key, receiver); }
1.2.2 统一的操作返回值
Reflect 方法返回布尔值(如 Reflect.set()
返回操作是否成功),简化了错误处理流程。
1.3 与 Vue2 的对比
特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy + Reflect) |
---|---|---|
属性监听 | 需遍历属性逐个劫持 | 代理整个对象,自动处理新增/删除属性 |
数组支持 | 需重写数组方法 | 直接拦截原生数组操作 |
性能 | 初始化递归遍历,性能较差 | 惰性代理,按需触发拦截 |
代码复杂度 | 需手动处理嵌套对象和数组 | 原生支持复杂数据结构 |
2 依赖收集与触发机制
Vue3 的依赖收集机制通过 Proxy 拦截访问 → 全局拓扑存储 → 精准触发更新 的链路,实现了高效、精准的响应式更新。其 WeakMap 结构、ReactiveEffect 双向链接和 惰性代理 等特性,使得框架具备更高的性能。
2.1 全局依赖存储结构:WeakMap → Map → Set
Vue3 使用三级数据结构管理依赖关系,确保高效的内存管理和精准的依赖追踪:
// 源码位置:packages/reactivity/src/effect.ts
type Dep = Set<ReactiveEffect>;
type KeyToDepMap = Map<any, Dep>;
const targetMap = new WeakMap<any, KeyToDepMap>(); // 全局依赖存储// 结构示意:
WeakMap {[targetObject]: Map {[key]: Set<effect1, effect2...>}
}
- WeakMap:键为原始对象(避免内存泄漏),值为
KeyToDepMap
。 - KeyToDepMap:键为对象属性名,值为
Dep
(存储关联的副作用函数集合)。 - Dep:
Set<ReactiveEffect>
,保证副作用的唯一性。
2.2 依赖收集触发时机
当访问响应式对象的属性时,Proxy 的 get
拦截器触发依赖收集流程:
// 源码简化:packages/reactivity/src/baseHandlers.ts
function createGetter() {return function get(target: object, key: string |