Vue3解析學習handlers 模塊
本文基于個人對源碼的理解與整理,難免存在偏差或不完整之處,如果你有更深入的見解或發(fā)現(xiàn)錯誤,期待一起探討與修正。
一、handlers 的核心設計目標
一句話總結:
不同數(shù)據(jù)結構,用不同的代理策略,做到“最小攔截 + 精確觸發(fā)”。
響應式系統(tǒng)本質(zhì)上是兩件事:
依賴收集(track):讀取時記錄依賴
副作用觸發(fā)(trigger):寫入時觸發(fā)更新
但不同類型的數(shù)據(jù):
變更方式不同
可攔截能力不同
因此 Vue 把代理邏輯拆成三套:
| 類型 | 變更方式 | Proxy 能力 | 方案 |
|---|---|---|---|
| Object | 屬性賦值 | 可完整攔截 | baseHandlers |
| Array | 屬性 + 方法 | 方法無法直接攔截 | 重寫數(shù)組方法 |
| Map/Set | 方法驅(qū)動 | 只能攔截 get | 返回重寫方法 |
這不是“復雜”,而是對 JavaScript 語義邊界的工程妥協(xié)。
二、Object:類繼承體系
Object 的 handler 在 baseHandlers.ts 中,通過類繼承組織:
1. BaseReactiveHandler(抽象基類)
這是統(tǒng)一入口,負責:
get 攔截
ref 解包
深/淺響應式遞歸
依賴收集
核心職責:
訪問屬性 → track → 返回響應式值
2. MutableReactiveHandler(可變對象)
這是最常見的 reactive 版本:
set
觸發(fā)兩種操作類型:
TriggerOpTypes.ADD(新增屬性)
TriggerOpTypes.SET(修改已有屬性)
區(qū)分 ADD / SET 的意義在于:
新增屬性 ≠ 修改屬性
某些依賴只關心結構變化(比如 for...in、Object.keys)。
deleteProperty
觸發(fā):
TriggerOpTypes.DELETE
has
用于:
key in obj
依賴收集:
TrackOpTypes.HAS
ownKeys
用于:
for...in / Object.keys / Reflect.ownKeys
依賴收集:
TrackOpTypes.ITERATE
這一步是很多人忽略的關鍵:
遍歷結構本身也是依賴。
3. ReadonlyReactiveHandler(只讀)
只讀版本直接阻斷寫操作:
set → 警告 + 不執(zhí)行
deleteProperty → 警告 + 不執(zhí)行
但 讀取仍然會 track。
因為只讀不等于“無依賴”。
三、Array:方法重寫策略
數(shù)組的問題在于:
大部分變更不是通過屬性賦值,而是通過方法。
例如:
arr.push() arr.splice() arr.shift()
Proxy 無法直接攔截方法調(diào)用。
Vue3 的方案是:
在 get trap 中返回“重寫版本”的數(shù)組方法。
arrayInstrumentations.ts
返回“重寫版本”的方法對象。
當訪問數(shù)組方法時:
proxy.push
BaseReactiveHandler 的 get trap 會判斷:
是不是數(shù)組? 是不是被重寫的方法?
如果是:
→ 返回”重寫版本“方法
這些方法內(nèi)部:
先暫停依賴收集(避免死循環(huán))
調(diào)用原始數(shù)組方法
手動 trigger
這就是:
用函數(shù)包裝模擬“方法攔截”。
四、Map / Set:工廠函數(shù)體系
集合類型比數(shù)組更極端:
所有變更都通過方法。
map.set() map.delete() set.add()
Proxy 能攔截的只有:
get
因此 Vue 采用純工廠函數(shù)模式,而不是類繼承。
1. 只攔截 get
collectionHandlers 的策略:
get → 返回重寫后的方法
和數(shù)組類似,但更徹底。
2. 四種 handler 組合
創(chuàng)建導出 4 個全局代理對象:
mutableCollectionHandlers(深響應式)
shallowCollectionHandlers(淺響應式)
readonlyCollectionHandlers(只讀)
shallowReadonlyCollectionHandlers(淺只讀)
這些不是手寫的,而是通過:
createInstrumentationsGetter(isReadonly, shallow)
動態(tài)生成。
3. createInstrumentations 核心工廠
這個工廠函數(shù)負責:
構造重寫后的 Map/Set 方法
在方法內(nèi)部訪問原始對象
關鍵點:
ReactiveFlags.RAW → 拿到原始數(shù)據(jù)
避免代理套代理導致的遞歸問題。
五、Track / Trigger 的精細控制
這是 Vue 3 響應式系統(tǒng)的靈魂改進之一。
讀取操作被分為:
GET
HAS
ITERATE
寫入操作被分為:
SET
ADD
DELETE
它們不是簡單的一對一關系。
精確觸發(fā)的意義
關鍵規(guī)則:
并不是所有寫操作都應該觸發(fā)所有依賴。
例如:
GET 依賴只關心 SET
ITERATE 依賴關心 ADD / DELETE
HAS 依賴只關心 key 是否存在
如果不區(qū)分:
任何寫操作 → 全部 effect 觸發(fā)
這會導致:
不必要的重渲染
性能雪崩
副作用風暴
Vue 2 的響應式就屬于粗粒度觸發(fā)。
Vue 3 的 handlers 設計本質(zhì)是:
把“讀類型”和“寫類型”建立映射關系。
到此這篇關于Vue3解析學習handlers 模塊的文章就介紹到這了,更多相關Vue3 handlers 模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue數(shù)據(jù)驅(qū)動表單渲染,輕松搞定form表單
這篇文章主要介紹了Vue數(shù)據(jù)驅(qū)動表單渲染,輕松搞定form表單,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-07-07
vue中三種插槽(默認插槽/具名插槽/作用域插槽)的區(qū)別詳解
默認插槽,具名插槽,作用域插槽是vue中常用的三個插槽,這篇文章主要為大家介紹了這三種插槽的使用與區(qū)別,感興趣的小伙伴可以了解一下2023-08-08

