| 特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) |
|---|---|---|
| 監(jiān)聽范圍 | 僅能監(jiān)聽已聲明屬性,新增屬性需 $set | 監(jiān)聽整個對象,自動支持新增/刪除屬性 |
| 數(shù)組支持 | 需重寫 push/splice 等方法 | 原生數(shù)組操作自動響應(yīng),無需額外處理 |
| 嵌套對象 | 需遞歸遍歷手動綁定 | 自動深層代理(非淺響應(yīng)式時) |
| 性能 | 初始化時遞歸遍歷所有屬性 | 懶代理(訪問時才代理嵌套對象) |
1、proxy的核心攔截能力
與直接操作對象不同,Proxy 會創(chuàng)建一個代理層,所有對數(shù)據(jù)的訪問和修改都需經(jīng)過這層攔截,從而實現(xiàn)依賴收集與更新觸發(fā)。
vue3主要使用一下幾種實現(xiàn)方式:
-
get攔截:訪問屬性時觸發(fā),用于依賴收集。當(dāng)模板或者watch訪問響應(yīng)式數(shù)據(jù)時,get攔截器會記錄當(dāng)前依賴(如組件渲染的函數(shù)),建立數(shù)據(jù)與依賴的關(guān)聯(lián)。 -
set攔截:修改屬性時觸發(fā),用于觸發(fā)更新。當(dāng)數(shù)據(jù)變化時,set攔截器會通知所有依賴(如重新渲染組件) -
deleteProperty攔截:刪除屬性時觸發(fā),確保刪除操作也能觸發(fā)響應(yīng)式更新。 -
has攔截:攔截in操作符(key in obj) 支持對屬性存在性的響應(yīng)式判斷
const obj = { count: 0 };
const proxy = new Proxy(obj, {
get(target, key) {
console.log(`訪問了 ${key}`); // 依賴收集
return Reflect.get(target, key); // 使用 Reflect 保持 this 指向正確
},
set(target, key, value) {
console.log(`修改了 ${key} 為 ${value}`); // 觸發(fā)更新
return Reflect.set(target, key, value);
}
});
proxy.count++; // 輸出:訪問了 count → 修改了 count 為 1
2、Object.defineProperty()的核心攔截能力
Vue 2 的響應(yīng)式系統(tǒng)核心依賴 Object.defineProperty 方法,通過劫持對象屬性的讀寫操作實現(xiàn)數(shù)據(jù)驅(qū)動視圖。其工作原理是遞歸遍歷 data 中的所有屬性,為每個屬性添加 getter/setter 攔截器,在屬性被訪問時收集依賴(如組件渲染函數(shù)),在屬性被修改時觸發(fā)更新(通知依賴重新執(zhí)行)
-
get 攔截:當(dāng)屬性被訪問時觸發(fā),用于收集依賴(如將當(dāng)前組件的 Watcher 添加到 Dep 依賴收集器)。 -
set 攔截:當(dāng)屬性被修改時觸發(fā),用于通知依賴更新(調(diào)用 Dep 的 notify 方法,觸發(fā)所有相關(guān) Watcher 重新執(zhí)行)
2.1、關(guān)鍵限制與解決方案
-
無法監(jiān)聽新增/刪除屬性
Object.defineProperty 僅能劫持初始化時存在的屬性,新增屬性(如 this.obj.newProp = 1)或刪除屬性(如 delete this.obj.prop)不會觸發(fā)響應(yīng)式更新。需通過 Vue.set(obj, key, value) 或 this.$delete(obj, key) 手動處理:- Vue.set 原理:對對象調(diào)用 defineReactive 添加 getter/setter,并觸發(fā) dep.notify()
-
數(shù)組響應(yīng)式的特殊處理
由于直接監(jiān)聽數(shù)組索引性能開銷大,Vue 2 采用重寫數(shù)組原型方法(push/pop/splice 等 7 個變異方法)的方式實現(xiàn)數(shù)組響應(yīng)式:- 攔截數(shù)組方法調(diào)用,執(zhí)行原生邏輯后觸發(fā) dep.notify(),并對新增元素遞歸執(zhí)行響應(yīng)式處理。
- 直接修改索引(如 arr[0] = 1)或長度(如 arr.length = 0)無法觸發(fā)更新,需使用 Vue.set(arr, index, value)。
深層嵌套對象的性能問題
初始化時需遞歸遍歷所有嵌套屬性,為每個屬性添加 getter/setter,當(dāng)數(shù)據(jù)層級過深(如 10 層以上)時,會導(dǎo)致初始化性能下降