概覽
vue3中reactive
用于將普通對象轉換為響應式對象,它的實現原理是通過Proxy
和Reflect
來實現的。具體的實現文件參見packages\reactivity\src\reactive.ts
。本文會介紹reactive
的相關api如下:
reactive
:將普通對象轉換為響應式對象readonly
:將普通對象轉換為只讀響應式對象isReactive
:判斷一個對象是否是響應式對象isReadonly
:判斷一個對象是否是只讀響應式對象isShallow
:判斷一個對象是否是淺層響應式對象isProxy
:判斷一個對象是否是代理對象shallowReactive
:創建一個淺層響應式對象shallowReadonly
:創建一個淺層只讀響應式對象markRaw
:標記一個對象為原始對象,避免被轉換為響應式對象toReadonly
:將一個響應式對象轉換為只讀響應式對象
源碼分析
在分析reactive.ts
的源碼之前,先了解如下幾個變量,它們分別是:
const reactiveMap = /* @__PURE__ */ new WeakMap(); // 響應式對象的緩存Map
const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); // 淺層響應式對象的緩存Map
const readonlyMap = /* @__PURE__ */ new WeakMap(); // 只讀響應式對象的緩存Map
const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); // 淺層只讀響應式對象的緩存Map
isReadonly
isReadonly
用于判斷一個對象是否是只讀響應式對象,它的實現如下:
function isReadonly(value) {return !!(value && value["__v_isReadonly"]);
}
isReadonly
就是判斷參數value
的__v_isReadonly
屬性值的布爾值,若為true
則表示是只讀對象;否則不是只讀對象。
reactive
reactive
的實現如下:
function reactive(target) {// 判斷target是否是只讀對象,若是,則直接返回if (isReadonly(target)) {return target;}// 調用 createReactiveObject 函數創建響應式對象,并返回return createReactiveObject(target, // 目標對象false, // 是否只讀,默認為falsemutableHandlers, // 普通對象的代理處理函數mutableCollectionHandlers, // 集合對象的代理處理函數reactiveMap // 響應式對象的緩存Map);
}
createReactiveObject
reactive
的核心實現是createReactiveObject
函數,它的實現如下:
function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {// 判斷target是否是對象,若不是,則彈出警告,并直接返回if (!isObject(target)) {{warn(`value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String(target)}`);}return target;}if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {return target;}const targetType = getTargetType(target);if (targetType === 0 /* INVALID */) {return target;}const existingProxy = proxyMap.get(target);if (existingProxy) {return existingProxy;}const proxy = new Proxy(target,targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);proxyMap.set(target, proxy);return proxy;
}function targetTypeMap(rawType) {switch (rawType) {case "Object":case "Array":return 1 /* COMMON */;case "Map":case "Set":case "WeakMap":case "WeakSet":return 2 /* COLLECTION */;default:return 0 /* INVALID */;}
}
function getTargetType(value) {return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value));
}
createReactiveObject
函數接受5個參數,依次為:目標對象target
、是否只讀isReadonly2
、基本類型處理函數baseHandlers
、集合對象的代理處理函數collectionHandlers
、響應式對象的緩存proxyMap
。
createReactiveObject
在確保target
為對象后,會檢測target
是一個響應式對象,若是則直接返回;然后會從緩存proxyMap
中獲取target
的代理對象,若存在則直接返回該代理對象;否則調用getTargetType
函數判斷目標對象target
的類型,若類型為INVALID
,則直接返回target
;否則根據類型創建代理對象,并將其緩存到proxyMap
中,最后返回該代理對象。
getTargetType
的實現也在上面,若target
存在__v_skip
屬性且為true
,或者target
不是可擴展的對象,則返回INVALID
;否則根據target
的類型調用targetTypeMap
函數返回類型。
由上targetTypeMap
函數可知,targetTypeMap
函數根據target
的類型返回一個數字,分別表示:
0
:INVALID
,表示target
不是一個對象1
:COMMON
,表示target
是一個普通對象或數組2
:COLLECTION
,表示target
是一個集合對象
綜上,可以理解createReactiveObject
函數根據target
創建代理對象的邏輯如下:
- 若
target
為普通對象或數組,則創建普通對象的代理對象,使用baseHandlers
處理函數; - 若
target
為Map
/Set
/WeakMap
/WeakSet
,則創建集合對象的代理對象,使用collectionHandlers
處理函數; - 其余情況,則直接返回
target
。
關于處理器baseHandlers
和collectionHandlers
的實現,會在后面的章節中介紹。
readonly
/shallowReactive
/shallowReadonly
readonly
/shallowReactive
/shallowReadonly
的實現如下:
function readonly(target) {return createReactiveObject(target,true, // 表示只讀readonlyHandlers, // 普通對象的只讀代理處理函數readonlyCollectionHandlers, // 集合對象的只讀代理處理函數readonlyMap // 只讀響應式對象的緩存readonlyMap);
}function shallowReactive(target) {return createReactiveObject(target,false, // 不是只讀shallowReactiveHandlers, // 普通對象的淺層響應式代理處理函數shallowCollectionHandlers, // 集合對象的淺層響應式代理處理函數shallowReactiveMap // 淺層響應式對象的緩存shallowReactiveMap);
}function shallowReadonly(target) {return createReactiveObject(target,true, // 表示只讀shallowReadonlyHandlers, // 普通對象的淺層只讀代理處理函數shallowReadonlyCollectionHandlers, // 集合對象的淺層只讀代理處理函數shallowReadonlyMap // 淺層只讀響應式對象的緩存shallowReadonlyMap);
}
readonly
的實現和reactive
的實現類似,不同就是調用createReactiveObject
函數時,傳參不同。
類似的還有shallowReactive
、shallowReadonly
。
它們的不同如下所示
函數 | 只讀 | 淺層 | 緩存 | 普通對象的代理方法 | 集合對象的代理方法 |
---|---|---|---|---|---|
reactive | 否 | 否 | reactiveMap | baseHandlers | collectionHandlers |
shallowReactive | 否 | 是 | shallowReactiveMap | shallowReactiveHandlers | shallowCollectionHandlers |
readonly | 是 | 否 | readonlyMap | readonlyHandlers | readonlyCollectionHandlers |
shallowReadonly | 是 | 是 | shallowReadonlyMap | shallowReadonlyHandlers | shallowReadonlyCollectionHandlers |
isReactive
isReactive
的實現如下:
function isReactive(value) {if (isReadonly(value)) {return isReactive(value["__v_raw"]);}return !!(value && value["__v_isReactive"]);
}
isReactive
會先判斷value
是否為只讀響應式數據,若為只讀響應式數據,則會遞歸調用isReactive
函數判斷value
的原始數據是否為響應式數據;否則,會判斷value
是否為響應式數據,若為響應式數據,則返回true
,否則返回false
。
isShallow
/ isProxy
/ isReadonly
function isShallow(value) {return !!(value && value["__v_isShallow"]);
}
function isProxy(value) {return value ? !!value["__v_raw"] : false;
}
function isReadonly(value) {return !!(value && value["__v_isReadonly"]);
}
isShallow
/isProxy
/isReadonly
的實現都比較簡單,都是判斷參數是否存在某個屬性,若存在則返回true
,否則返回false
。它們讀取的屬性__v_isShallow
/__v_raw
/__v_isReadonly
實際上都是針對代理對象的,而它們的邏輯處理也是在處理器方法中實現的,后續會講到
markRaw
markRaw
用于標記對象為原始對象,這種對象不會被轉換為響應式對象,也不會被代理,其實現如下:
function markRaw(value) {if (!hasOwn(value, "__v_skip") && Object.isExtensible(value)) {def(value, "__v_skip", true);}return value;
}
markRaw
首先會判斷對象是否可擴展,若可擴展,則會在對象上定義一個__v_skip
屬性,打一個標記,值為true
;最后返回該對象。使用reactive(target)
創建響應式對象時,若target
不可擴展,則在調用createReactiveObject
時,其內部調用getTargetType
的返回值就是0
.
def
內部就是調用Object.defineProperty
定義對象上屬性
toReadonly
toReadonly
用于將響應式對象轉換為只讀對象,其實現如下:
const toReadonly = (value) => isObject(value) ? readonly(value) : value;
toReadonly
會判斷參數是否為對象,若為是,則會調用readonly
函數將參數轉換為只讀響應式對象,并返回該代理對象;否則,直接返回參數。這和toReactive
很類似,只是toReactive
會將參數轉換為響應式對象,而toReadonly
會將參數轉換為只讀響應式對象。