《Vue3 从入门到大神05篇》Vue3 响应式原理初探 —— 从 Object.defineProperty 到 Proxy
📌 前言
如果说 Composition API 是 Vue3 的表象革新,那么:
响应式系统的重构,才是 Vue3 的灵魂升级。
Vue2 的响应式被无数开发者“吐槽过”:
-
❌ 无法监听对象新增属性
-
❌ 无法监听数组索引变化
-
❌
$set/$delete用得心累
而 Vue3 用 Proxy + Reflect 一次性解决了这些问题。
今天这篇文章,我们就从原理图 → 代码对比 → 手写简易版,彻底搞懂 Vue3 的响应式核心。
一、Vue2 响应式:Object.defineProperty 的局限
1️⃣ Vue2 响应式简化模型
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
// 依赖收集
return val
},
set(newVal) {
// 通知更新
val = newVal
}
})
}
2️⃣ Vue2 的致命问题
|
问题 |
表现 |
|---|---|
|
新增属性 |
无法监听(需 |
|
删除属性 |
无法监听(需 |
|
数组索引 |
无法监听 |
|
性能 |
初始化递归遍历所有属性 |
📌 根本原因:
Object.defineProperty只能劫持已有属性。
二、Vue3 响应式:Proxy 的降维打击
1️⃣ Proxy 是什么?
一句话解释:
Proxy 是对整个对象的“代理”,而不是对单个属性的“修补”。
const obj = { count: 0 }
const proxy = new Proxy(obj, {
get(target, key, receiver) {
console.log('get:', key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('set:', key, value)
return Reflect.set(target, key, value, receiver)
}
})
2️⃣ 拦截能力对比(重点)
|
行为 |
Vue2 |
Vue3 |
|---|---|---|
|
读取属性 |
✅ |
✅ |
|
新增属性 |
❌ |
✅ |
|
删除属性 |
❌ |
✅ |
|
数组索引 |
❌ |
✅ |
|
length 变化 |
❌ |
✅ |
|
Map / Set |
❌ |
✅ |
✅ Proxy 能拦截 13 种操作,几乎覆盖所有对象行为。
三、Reflect:为什么 Vue3 一定要用它?
1️⃣ Reflect 的作用
|
能力 |
说明 |
|---|---|
|
统一 API |
替代 |
|
返回值语义 |
成功 / 失败 |
|
与 Proxy 完美配合 |
保证 receiver 正确指向 |
2️⃣ 错误示例 vs 正确示例
❌ 不推荐:
get(target, key) {
return target[key]
}
✅ Vue3 推荐:
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
}
📌 原因:
-
保证
this指向正确 -
兼容原型链
-
行为更规范
四、Vue3 响应式原理图解(文字版)
组件渲染
↓
读取响应式数据(get)
↓
依赖收集(track)
↓
建立 数据 ↔ 组件 关联
↓
数据变化(set)
↓
触发依赖(trigger)
↓
组件重新渲染
📌 核心函数:
-
reactive()→ 创建响应式对象 -
effect()→ 副作用函数(组件渲染) -
track()→ 依赖收集 -
trigger()→ 触发更新
五、手写一个 Mini Vue3 响应式(面试高频)
1️⃣ 实现 reactive
function reactive(obj) {
return new Proxy(obj, {
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
}
})
}
2️⃣ 简化版 effect / track / trigger
const targetMap = new WeakMap()
function track(target, key) {
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) return
const effects = depsMap.get(key)
effects?.forEach(fn => fn())
}
let effect
effect = () => {
console.log('组件重新渲染')
}
✅ 这就是 Vue3 响应式的最小实现模型。
六、Vue3 响应式的三大优势总结
✅ 1. 真正的数据监听
const state = reactive({ count: 0 })
state.newProp = 123 // ✅ 可监听
✅ 2. 性能更好
-
初始化时不递归
-
访问时才代理
✅ 3. 代码更优雅
-
不再需要
$set/$delete -
心智负担大幅降低
七、面试高频问答(建议背)
Q1:Vue2 为什么不能监听新增属性?
因为
Object.defineProperty只能劫持已有属性。
Q2:Vue3 为什么用 Proxy?
Proxy 可以拦截整个对象,支持新增、删除、数组变化。
Q3:Reflect 的作用是什么?
提供统一的对象操作 API,保证 Proxy 行为正确。
更多推荐
所有评论(0)