HoRain云--深入解析Vue3响应式原理
·

🎬 HoRain 云小助手:个人主页
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
目录

Vue 3 的响应式系统是其核心特性之一,基于 Proxy 代替了 Vue 2 的 Object.defineProperty,实现了更高效、更灵活的响应式机制。以下是其原理的深度解析,包含关键设计思想和实现逻辑:
一、核心原理:Proxy + 依赖收集 + 副作用调度
1. 为什么用 Proxy?
- Vue 2 的缺陷:
- 无法检测对象属性的新增/删除(需
Vue.set/Vue.delete)。 - 无法检测数组的索引修改(如
arr[0] = ...)。 - 无法监听原始值(如
ref需单独处理)。
- 无法检测对象属性的新增/删除(需
- Vue 3 的改进:
- Proxy 能拦截对象的所有操作(属性读写、
in操作、delete等),无需额外 API。 - 支持原始值(通过
ref包装)和嵌套对象的响应式。
- Proxy 能拦截对象的所有操作(属性读写、
二、核心实现流程(以 reactive 为例)
步骤 1:创建响应式代理
const data = reactive({ count: 0 }) // 通过 Proxy 包装对象
reactive内部:function reactive(target) { return new Proxy(target, { get(target, key, receiver) { // 依赖收集(track) track(target, key) return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { // 触发更新(trigger) const result = Reflect.set(target, key, value, receiver) trigger(target, key) return result } }) }
步骤 2:依赖收集(Track)
- 当读取响应式数据时(如
data.count):- 触发
get拦截器。 - 通过
track将当前执行的副作用函数(如effect)与当前数据属性(count)关联。 - 依赖存储:使用
WeakMap(target→Map→key→Set<effect>)。
- 触发
关键数据结构:
// 依赖存储 const targetMap = new WeakMap() // { target: map } // 每个 target 的依赖 const keyMap = new Map() // { key: effectSet } // 每个 key 的依赖集合 const effectSet = new Set() // { effectFn1, effectFn2 }
步骤 3:触发更新(Trigger)
- 当修改响应式数据时(如
data.count++):- 触发
set拦截器。 - 通过
trigger通知所有依赖当前 key 的副作用函数执行。 - 执行副作用函数(如重新渲染组件)。
- 触发
三、关键设计:副作用函数(Effect)
1. effect 函数(核心)
effect(() => {
console.log(data.count) // 依赖收集:data.count ← effect
})
- 执行逻辑:
- 保存当前
effect到全局变量activeEffect。 - 执行传入的函数(触发
get读取响应式数据)。 - 依赖收集:在
get中将activeEffect添加到targetMap。 - 恢复
activeEffect为上一个值。
- 保存当前
2. 依赖去重与自动清理
- 通过
Set存储副作用函数,避免重复添加。 - 依赖收集仅在
effect执行时触发,避免冗余。
四、与 Vue 2 的关键对比
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 核心机制 | Object.defineProperty |
Proxy |
| 新增/删除属性 | ❌ 需 Vue.set/Vue.delete |
✅ 自动支持 |
| 数组索引修改 | ❌ 需 Vue.set |
✅ 自动支持 |
| 原始值响应式 | ❌ 需 ref |
✅ ref 直接包裹原始值 |
| 依赖收集 | 通过 dep 数组 |
通过 WeakMap + Set |
| 性能 | 低(需遍历对象属性) | 高(仅收集实际访问的属性) |
💡 示例:
Vue 2 中obj.newProp = 1不会触发更新,Vue 3 通过Proxy的set拦截自动触发。
五、ref 与 reactive 的关系
ref:包装原始值(如number,string)为响应式对象。const count = ref(0) // { value: 0 } // 读取时自动解包:count.valuereactive:包装对象为响应式对象。const state = reactive({ count: 0 }) // { count: 0 } // 读取时直接:state.count- 内部关联:
ref会调用reactive将value属性包装为响应式对象。
六、性能优化:调度与异步更新
1. 副作用调度(Scheduler)
- 默认行为:副作用函数同步执行(立即触发更新)。
- 优化:通过
scheduler参数异步调度(如 Vue 3 组件更新使用nextTick)。effect( () => { /* ... */ }, { scheduler: (job) => queueJob(job) } // 异步执行 )
2. 批量更新(Batching)
queueJob:将多个副作用函数合并为一次执行,避免重复渲染。// 例如:同时修改多个响应式属性 data.count++ data.msg = "updated" // 仅触发一次更新(Vue 3 默认行为)
七、实际执行流程(图示)
graph LR
A[读取响应式数据] -->|触发 get| B[track 依赖收集]
B --> C[将 effect 添加到 targetMap]
D[修改响应式数据] -->|触发 set| E[trigger 触发更新]
E --> F[遍历 targetMap 中 key 的 effect 集合]
F --> G[执行副作用函数]
G --> H[更新视图/执行逻辑]
✅ 总结:Vue 3 响应式系统优势
- 全面支持:对象、数组、原始值均自动响应。
- 性能更高:仅收集实际访问的属性(Vue 2 需遍历所有属性)。
- 代码更简洁:无需
Vue.set/Vue.delete,减少开发者心智负担。 - 灵活性强:通过
scheduler自定义更新策略(如异步、批量)。
💡 一句话理解:
Vue 3 用 Proxy 作为“数据看门人”,在读取/修改时自动记录依赖和触发更新,实现“数据变化→自动更新视图”的闭环。
附:关键源码片段(简化版)
// 依赖收集
function track(target, key) {
const effect = activeEffect
if (effect) {
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
dep.add(effect)
}
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (depsMap) {
const effects = depsMap.get(key)
if (effects) {
effects.forEach(effect => effect())
}
}
}
以上是 Vue 3 响应式系统的核心骨架,实际实现更复杂(如嵌套响应式、深层监听等),但原理一致。理解此机制能帮助你更高效地使用 Vue 3 的响应式 API(如
computed,watch)。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙
更多推荐


所有评论(0)