文章目錄
- 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 |