Vue 3 响应式原理详解
·
Vue 3 响应式原理详解
Vue 3 的响应式系统完全重写,使用 Proxy 替代了 Vue 2 的 Object.defineProperty,提供了更好的性能和更强大的功能。
1. 核心原理概述
Vue 2 的局限性
// Vue 2 使用 Object.defineProperty
Object.defineProperty(obj, 'key', {
get() {
// 依赖收集
return value
},
set(newVal) {
// 触发更新
value = newVal
}
})
问题:
- 无法检测对象属性的添加/删除
- 数组变异方法需要重写
- 需要递归遍历整个对象
- 性能开销较大
Vue 3 的解决方案 - Proxy
const reactive = (target) => {
return new Proxy(target, {
get(obj, key, receiver) {
// 依赖收集
const result = Reflect.get(obj, key, receiver)
return result
},
set(obj, key, value, receiver) {
// 触发更新
const result = Reflect.set(obj, key, value, receiver)
return result
},
deleteProperty(obj, key) {
// 处理属性删除
const result = Reflect.deleteProperty(obj, key)
return result
}
})
}
2. 响应式系统三大核心
2.1 响应式对象创建 (reactive)
// 简化版的 reactive 实现
function reactive(target) {
return new Proxy(target, {
get(obj, key, receiver) {
// 追踪依赖
track(obj, key)
const result = Reflect.get(obj, key, receiver)
// 如果是对象,递归代理
if (typeof result === 'object' && result !== null) {
return reactive(result)
}
return result
},
set(obj, key, value, receiver) {
const oldValue = obj[key]
const result = Reflect.set(obj, key, value, receiver)
// 只有值发生变化时才触发更新
if (oldValue !== value) {
trigger(obj, key)
}
return result
},
deleteProperty(obj, key) {
const hadKey = Object.prototype.hasOwnProperty.call(obj, key)
const result = Reflect.deleteProperty(obj, key)
if (hadKey) {
trigger(obj, key)
}
return result
}
})
}
2.2 依赖收集 (track)
// 存储所有依赖关系的全局变量
const targetMap = new WeakMap()
let activeEffect = null
function track(target, key) {
if (!activeEffect) return
// 获取 target 对应的依赖映射
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
// 获取 key 对应的依赖集合
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
// 添加当前活动的 effect
dep.add(activeEffect)
}
2.3 触发更新 (trigger)
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
// 执行所有相关的 effect
dep.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
})
}
}
3. 完整的响应式系统实现
3.1 Effect 系统
// 响应式副作用系统
function effect(fn, options = {}) {
const effectFn = () => {
// 清理依赖
cleanup(effectFn)
// 设置当前活动的 effect
activeEffect = effectFn
effectStack.push(effectFn)
// 执行原始函数
const result = fn()
// 恢复之前的 effect
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return result
}
// 存储依赖
effectFn.deps = []
effectFn.options = options
// 懒执行
if (!options.lazy) {
effectFn()
}
return effectFn
}
function cleanup(effectFn) {
for (const dep of effectFn.deps) {
dep.delete(effectFn)
}
effectFn.deps.length = 0
}
3.2 计算属性 (computed)
function computed(getter) {
let value
let dirty = true
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true
trigger(obj, 'value')
}
})
const obj = {
get value() {
if (dirty) {
value = effectFn()
dirty = false
}
track(obj, 'value')
return value
}
}
return obj
}
3.3 引用类型响应式 (ref)
function ref(value) {
return {
get value() {
track(this, 'value')
return value
},
set value(newVal) {
if (newVal !== value) {
value = newVal
trigger(this, 'value')
}
},
__v_isRef: true
}
}
4. 实际应用示例
4.1 响应式数据流
import { reactive, effect } from 'vue'
const state = reactive({
count: 0,
message: 'Hello'
})
// 副作用函数 - 自动追踪依赖
effect(() => {
console.log(`Count: ${state.count}, Message: ${state.message}`)
})
// 触发更新
state.count = 1 // 输出: Count: 1, Message: Hello
state.message = 'World' // 输出: Count: 1, Message: World
4.2 计算属性实现
import { reactive, computed, effect } from 'vue'
const state = reactive({
price: 10,
quantity: 2
})
const total = computed(() => state.price * state.quantity)
effect(() => {
console.log(`Total: $${total.value}`)
})
state.price = 20 // 输出: Total: $40
state.quantity = 3 // 输出: Total: $60
5. 性能优化特性
5.1 懒代理
function reactive(target) {
return new Proxy(target, {
get(obj, key, receiver) {
track(obj, key)
const result = Reflect.get(obj, key, receiver)
// 只有访问时才进行代理,不是一次性递归代理所有
if (typeof result === 'object' && result !== null) {
return reactive(result)
}
return result
}
// ... 其他 trap
})
}
5.2 调度系统
const queue = new Set()
let isFlushing = false
function queueJob(job) {
queue.add(job)
if (!isFlushing) {
isFlushing = true
Promise.resolve().then(() => {
try {
queue.forEach(job => job())
} finally {
queue.clear()
isFlushing = false
}
})
}
}
6. 与 Vue 2 的对比
| 特性 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
|---|---|---|
| 检测属性添加/删除 | ❌ 需要 Vue.set/Vue.delete | ✅ 原生支持 |
| 数组索引修改 | ❌ 需要重写方法 | ✅ 原生支持 |
| 数组长度修改 | ❌ 无法检测 | ✅ 原生支持 |
| Map/Set 支持 | ❌ 有限支持 | ✅ 完整支持 |
| 性能 | 递归遍历,初始化慢 | 懒代理,按需响应 |
| 内存占用 | 较高 | 较低 |
7. 总结
Vue 3 的响应式原理核心:
- Proxy 代理:使用 ES6 Proxy 拦截对象操作
- 依赖收集:在 get 操作时收集依赖的 effect
- 触发更新:在 set 操作时触发相关的 effect 重新执行
- Effect 系统:管理副作用的执行和清理
- 性能优化:懒代理、调度队列、缓存等机制
这种设计使得 Vue 3 的响应式系统更强大、性能更好,并且能够处理更多复杂场景。
更多推荐


所有评论(0)