Vue3?源碼分析reactive?readonly實(shí)例
引言
上次一起閱讀了watch和computed的源碼,其實(shí)應(yīng)該先看副作用effect,因?yàn)楦鱾€(gè)響應(yīng)式的API里基本都用到了,等結(jié)束了reactive和readonly和ref,就一起看看effect。這次要說的是reactive和readonly,兩者在實(shí)現(xiàn)上流程大體一致。尤其是對Map和Set的方法的代理攔截,多少有點(diǎn)妙。
一、reactive 和 readonly
Vue3使用Proxy來替代Vue2中Object.defineProperty。
const target = {
name: 'onlyy~'
}
// 創(chuàng)建一個(gè)對target的代理
const proxy = new Proxy(target, {
// ...各種handler,例如get,set...
get(target, property, receiver){
// 其它操作
// ...
return Reflect.get(target, property, receiver)
}
})
1. reactive相關(guān)類型
reactive利用Proxy來定義一個(gè)響應(yīng)式對象。
- Target:目標(biāo)對象,包含幾個(gè)標(biāo)志,以及__v_raw字段,該字段表示它原本的非響應(yīng)式狀態(tài)的值;
export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.IS_SHALLOW]?: boolean
[ReactiveFlags.RAW]?: any
}
export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()
const enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2
}
2. 相關(guān)全局變量與方法
- ReactiveFlags:定義了各種標(biāo)志對應(yīng)的字符串(作為reactive對象的屬性)的枚舉;
- reactiveMap
- shallowReactiveMap
- readonlyMap
- shallowReadonlyMap:這幾個(gè)Map分別用于存放對應(yīng)API生成的響應(yīng)式對象(以目標(biāo)對象為key,代理對象為value),便于后續(xù)判斷某個(gè)對象是否存在已創(chuàng)建的響應(yīng)式對象;
- TargetType:枚舉成員的內(nèi)容分別用于區(qū)分代理目標(biāo)是否校驗(yàn)合法、普通對象、Set或Map;
// 各個(gè)標(biāo)志枚舉
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
IS_SHALLOW = '__v_isShallow',
RAW = '__v_raw'
}
// ...
export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()
const enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2
}
然后是兩個(gè)函數(shù):targetTypeMap用于判斷各種JS類型屬于TargetType中的哪種;getTargetType用于獲取target對應(yīng)的TargetType類型。
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default:
return TargetType.INVALID
}
}
function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}
3. reactive函數(shù)
reactive入?yún)㈩愋蜑閛bject,返回值類型是UnwrapNestedRefs,對嵌套的Ref進(jìn)行了解包。意味著即使reactive接收一個(gè)Ref,其返回值也不用再像Ref那樣通過.value來讀取值。源碼的注釋中也給出了示例。
/*
* const count = ref(0)
* const obj = reactive({
* count
* })
*
* obj.count++
* obj.count // -> 1
* count.value // -> 1
*/
reactive內(nèi)部調(diào)用createReactiveObject來創(chuàng)建響應(yīng)式對象。瞄一眼入?yún)⒂形鍌€(gè):
- target:代理目標(biāo);
- false:對應(yīng)createReactiveObject的isReadonly參數(shù);
- mutableHandlers:普通對象和數(shù)組的代理處理程序;
- mutableCollectionHandlers:Set和Map的代理處理程序;
- reactiveMap:之前定義的全局變量,收集reactive對應(yīng)的依賴。
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
4. 造物主createReactiveObject
不論是reactive,還是shallowReactive、readonly和shallowReadonly,都是內(nèi)部調(diào)用createReactiveObject來創(chuàng)建代理的。createReactiveObject也沒什么操作,主要判斷了下target的類型,再決定是直接返回target還是返回一個(gè)新建的proxy。
以下情況直接返回target:
- target不是對象;
- target已經(jīng)是一個(gè)響應(yīng)式的對象,即由createReactiveObject創(chuàng)建的proxy;
- target類型校驗(yàn)不合法,例如RegExp、Date等;
當(dāng)參數(shù)proxyMap對應(yīng)的實(shí)參(可能為reactiveMap、shallowReactiveMap、readonlyMap或shallowReadonlyMap,分別對應(yīng)ractive、shallowReactive、readonly和shallowReadonly四個(gè)API)里已經(jīng)存在了target的響應(yīng)式對象時(shí),直接取出并返回該響應(yīng)式對象;
否則,創(chuàng)建一個(gè)target的響應(yīng)式對象proxy,將proxy加入到proxyMap中,然后返回該proxy。
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
我們知道,代理的重點(diǎn)其實(shí)在與代理的處理程序,createReactiveObject根據(jù)普通對象和數(shù)組類型、Set和Map類型來區(qū)分baseHandlers和collectionHandlers。
5. shallowReactive、readonly和shallowReadonly
事實(shí)上,ractive、shallowReactive、readonly和shallowReadonly這幾個(gè)函數(shù)形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應(yīng)式對象,存儲在對應(yīng)的proxyMap里,但是對應(yīng)的baseHandlers和collectionHandlers有區(qū)別。
// shallowReactive
export function shallowReactive<T extends object>(
target: T
): ShallowReactive<T> {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap
)
}
// raedonly
// 注意readonly不是響應(yīng)式的,而是一個(gè)原對象的只讀的拷貝
// 具體實(shí)現(xiàn)在對應(yīng)的handlers里
export function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers,
readonlyMap
)
}
// shallowReadonly
// 是響應(yīng)式的
// 只有最外層是只讀的
export function shallowReadonly<T extends object>(target: T): Readonly<T> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers,
shallowReadonlyMap
)
}
事實(shí)上,ractive、shallowReactive、readonly和shallowReadonly這幾個(gè)函數(shù)形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應(yīng)式對象,存儲在對應(yīng)的proxyMap里,但是對應(yīng)的baseHandlers和collectionHandlers有區(qū)別。那么我們就知道了,其實(shí)重點(diǎn)都在各種handlers里。
二、對應(yīng)的 Handlers
baseHandlers用于普通對象和數(shù)組的代理,collectionHandlers用于Set、Map等的代理。對應(yīng)ractive、shallowReactive、readonly和shallowReadonly四個(gè)API,每一個(gè)都有自己的baseHandlers和collectionHandlers。
1. baseHandlers
在packages/reactivity/src/baseHandlers.ts文件中。分別導(dǎo)出了這4個(gè)API對應(yīng)的baseHandlers。
1.1 reactive
reactive的baseHandlers中有5個(gè)代理程序。
// reactive
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
在攔截過程中,在get、has和ownKey這幾個(gè)訪問程序中進(jìn)行依賴捕獲(track),在set和deleteProperty這倆用于更改的程序中觸發(fā)更新(trigger) 。
get和set分別由函數(shù)createGetter和createSetter創(chuàng)建,這倆函數(shù)根據(jù)入?yún)⒌牟煌?,返回不同的get和set,readonly等API的baseHandlers中的get和set也大都源于此,除了兩種readonly中用于告警的set。
(1) get
createGetter兩個(gè)入?yún)ⅲ篿sReadonly和isShallow,兩兩組合正好對應(yīng)四個(gè)API。
- shallow:為true時(shí)不會進(jìn)入遞歸環(huán)節(jié),因此是淺層的處理;
- isReadonly:在createGetter中影響proxyMap的選擇和遞歸時(shí)API的選擇,它主要發(fā)揮作用是在set中。
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// 以下幾個(gè)if分支判斷target是否已經(jīng)是由這幾個(gè)API創(chuàng)建的代理對象,代理得到的proxy才具有這些key
if (key === ReactiveFlags.IS_REACTIVE) {
// 是否是響應(yīng)式對象
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 是否是只讀對象
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
// 是否是淺層的 響應(yīng)式/只讀 對象
return shallow
} else if (
// __v_raw 屬性對應(yīng) 代理對象的目標(biāo)對象
// 當(dāng)該屬性有值,且在相應(yīng)的proxyMap中存在代理對象時(shí),說明target已經(jīng)是一個(gè)proxy了
// __v_raw 屬性對應(yīng)的值為target本身
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
const targetIsArray = isArray(target)
// 對數(shù)組的幾個(gè)方法進(jìn)行代理,在'includes', 'indexOf', 'lastIndexOf'等方法中進(jìn)行track捕獲依賴
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 如果不是readonly,則捕獲依賴,因此,readonly 為非響應(yīng)式的
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
if (shallow) {
return res
}
// 如果get到的值是一個(gè)Ref,會直接解包,無需再使用 .value 來獲取真正需要的值
// 除非目標(biāo)對象target是數(shù)組,或者當(dāng)前的key是整數(shù)
// 例如,obj[0],即使是一個(gè)Ref也不會直接解包,使用的時(shí)候依然要 obj[0].value
// shallow沒有走到這一步,因此也不會自動(dòng)解包
if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 當(dāng)get到的值是對象時(shí),根據(jù)是否是readonly來遞歸操作,需要防止對象循環(huán)引用
// shallow沒有走到這一步,因此shallow是淺層的
if (isObject(res)) {
// Convert returned value into a proxy as well. we do the isObject check
// here to avoid invalid value warning. Also need to lazy access readonly
// and reactive here to avoid circular dependency.
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
(2) set
對于reactive,可以說最主要的任務(wù)就是在set中觸發(fā)更新,set包括 新增 和 修改 屬性值。如果當(dāng)前的key對應(yīng)的值是一個(gè)Ref,且其它條件滿足時(shí),則觸發(fā)更新的操作是在Ref的內(nèi)部。這些在后續(xù)講解Ref的時(shí)候會提到。
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
let oldValue = (target as any)[key]
// 當(dāng)前值是Readonly的Ref,而新值不是Ref時(shí),不允許修改
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
// 如果是深層的修改
if (!shallow) {
// 解出原本的非proxy值
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
// 目標(biāo)對象非數(shù)組,當(dāng)前key的值是Ref而新值不是Ref,則通過 .value 賦值
// 在Ref內(nèi)部觸發(fā)更新
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
} else {
// 淺層模式下,忽略對象是否是響應(yīng)式的
// in shallow mode, objects are set as-is regardless of reactive or not
}
// 然后是觸發(fā)更新的部分了
// 判斷當(dāng)前key是否已經(jīng)存在于target上
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 如果是原型鏈上的字段則不會觸發(fā)更新
if (target === toRaw(receiver)) {
if (!hadKey) {
// 當(dāng)前的key已經(jīng)存在,觸發(fā)新增的更新
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 當(dāng)前key不存在,觸發(fā)修改的更新
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
(3) deleteProperty
刪除操作的代理程序,和set一樣,deleteProperty攔截delete和Reflect.deleteProperty()操作,它也能觸發(fā)更新。
function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key)
const oldValue = (target as any)[key]
const result = Reflect.deleteProperty(target, key)
// 刪除成功 且 target中原來有這個(gè)屬性時(shí),觸發(fā)刪除的更新
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
(4) has
has用于判斷target中是否有當(dāng)前的key,攔截a in obj、with(obj){(a)}、Reflect.has等操作,屬于訪問程序,在其中進(jìn)行has操作的依賴收集。
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key)
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, TrackOpTypes.HAS, key)
}
return result
}
(5) ownKeys
用于獲取target所有自身擁有的key,攔截Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、Reflect.ownKeys,屬于訪問程序,在其中進(jìn)行迭代的依賴收集。
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
現(xiàn)在我們算是都弄明白了,對于普通對象和數(shù)組,reactive創(chuàng)建proxy,通過get、set、deleteProperty、has、ownKeys五個(gè)代理處理程序,來攔截其屬性訪問操作,在其中進(jìn)行依賴收集,攔截其增刪改操作,其中觸發(fā)更新。
1.2 readonly
readonly的代理處理程序只有三個(gè):
- get:由createGetter(true)創(chuàng)建,還記得我們上面講到的createSetter嗎?
- set
- deleteProperty:這兩個(gè)代理處理程序用于告警,畢竟readonly不可修改。
畢加思索一下createGetter(true),傳入的readonly=true,使得get中不會進(jìn)行track操作來收集依賴,因而不具有響應(yīng)性。
const readonlyGet = /*#__PURE__*/ createGetter(true)
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
set(target, key) {
if (__DEV__) {
warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
},
deleteProperty(target, key) {
if (__DEV__) {
warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
}
}
1.3 shallowReactive
shallowReactive移植了reactive的baseHandlers,并且更新了get和set。具體實(shí)現(xiàn)也可以回顧上面說到的createGetter和createSetter。
回過頭來看看createGetter(false, true),isReadonly = false,則在get中,可以進(jìn)行track依賴收集;shallow = true,則在get中不會對頂層的Ref進(jìn)行解包,也不會進(jìn)行遞歸操作。
而在createSetter(true)中,參數(shù)shallow幾乎只影響是否要解出原本的raw值。如果新值value不是淺層且不是只讀的,則需要解出它的原本raw值,之后才能進(jìn)行賦值操作,否則我們的shallowRef將不再是淺層的了。
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const shallowSet = /*#__PURE__*/ createSetter(true)
export const shallowReactiveHandlers = /*#__PURE__*/ extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet
}
)
1.4 shallowReadonly
移植了readonly的baseHandlers,更新了其中的get,這個(gè)get也試試由createGetter創(chuàng)建。我們知道,readonly的baseHandlers里,除了get,另外倆都是用來攔截修改操作并告警的。
回顧一下createGetter,當(dāng)isReadonly===true時(shí),不會進(jìn)行track操作來收集依賴;shallow===true時(shí),不會對Ref進(jìn)行解包,也不會走到遞歸環(huán)節(jié),即是淺層的readonly。
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
// Props handlers are special in the sense that it should not unwrap top-level
// refs (in order to allow refs to be explicitly passed down), but should
// retain the reactivity of the normal readonly object.
export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
{},
readonlyHandlers,
{
get: shallowReadonlyGet
}
)
2. cellectionHandlers
對于Set和Map較為復(fù)雜的數(shù)據(jù)結(jié)構(gòu),他們有自己的方法,因此代理程序會有些差別。基本都是攔截它們原本的方法,然后進(jìn)行track或trigger??梢钥吹竭@幾個(gè)handlers中,都只有由createInstrumentationGetter創(chuàng)建的get。
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}
export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: /*#__PURE__*/ createInstrumentationGetter(false, true)
}
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: /*#__PURE__*/ createInstrumentationGetter(true, false)
}
export const shallowReadonlyCollectionHandlers: ProxyHandler<CollectionTypes> =
{
get: /*#__PURE__*/ createInstrumentationGetter(true, true)
}
1.1 createInstrumentationGetter
因?yàn)槭谴鞸et和Map,在攔截它們的實(shí)例方法之前,對實(shí)例的訪問,即get,這個(gè)get并非Map或Set實(shí)例的get方法,而是表示對實(shí)例的訪問操作。
例如:
const map = new Map([['name', 'cc']]);
map.set('age', 18);
這里map.set()首先就是訪問map的set方法,對應(yīng)的key就是字符串'set',而這一步就會被代理的get程序攔截,而真正的對方法的攔截,都在相應(yīng)的instrumentations里預(yù)設(shè)好了。攔截了之后,如果key在instrumentations里存在,返回預(yù)設(shè)的方法,在其中進(jìn)行track和trigger操作,否則是其它屬性/方法,直接返回即可,不會進(jìn)行track和trigger。
const [
mutableInstrumentations,
readonlyInstrumentations,
shallowInstrumentations,
shallowReadonlyInstrumentations
] = /* #__PURE__*/ createInstrumentations()
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
const instrumentations = shallow
? isReadonly
? shallowReadonlyInstrumentations
: shallowInstrumentations
: isReadonly
? readonlyInstrumentations
: mutableInstrumentations
return (
target: CollectionTypes,
key: string | symbol,
receiver: CollectionTypes
) => {
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.RAW) {
return target
}
return Reflect.get(
hasOwn(instrumentations, key) && key in target
? instrumentations
: target,
key,
receiver
)
}
}
1.2 instrumentations
和baseHandlers相比,Proxy無法直接攔截Map和Set的方法的調(diào)用,而是通過get程序來攔截,再判斷key是否為執(zhí)行增刪改查的方法,從而判斷是否進(jìn)行依賴收集或更新。因此,就需要先預(yù)設(shè)好,哪些key作為方法名時(shí)可以觸發(fā)track和trigger。其實(shí)也就是Map和Set的那些實(shí)例方法和迭代器方法。而各種Instrumentations,就是這些預(yù)設(shè)的方法,track和trigger操作都在其中。
function createInstrumentations() {
// 對應(yīng)reactive
const mutableInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key)
},
get size() {
return size(this as unknown as IterableCollections)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false, false)
}
// 對應(yīng)shallowReactive
const shallowInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, false, true)
},
get size() {
return size(this as unknown as IterableCollections)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false, true)
}
// 對應(yīng)readonly
const readonlyInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, true)
},
get size() {
return size(this as unknown as IterableCollections, true)
},
has(this: MapTypes, key: unknown) {
return has.call(this, key, true)
},
add: createReadonlyMethod(TriggerOpTypes.ADD),
set: createReadonlyMethod(TriggerOpTypes.SET),
delete: createReadonlyMethod(TriggerOpTypes.DELETE),
clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
forEach: createForEach(true, false)
}
// 對應(yīng)shallowReadonly
const shallowReadonlyInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key, true, true)
},
get size() {
return size(this as unknown as IterableCollections, true)
},
has(this: MapTypes, key: unknown) {
return has.call(this, key, true)
},
add: createReadonlyMethod(TriggerOpTypes.ADD),
set: createReadonlyMethod(TriggerOpTypes.SET),
delete: createReadonlyMethod(TriggerOpTypes.DELETE),
clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
forEach: createForEach(true, true)
}
// 使用 createIterableMethod 給這些 Instrumentations 掛上幾個(gè)迭代器
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach(method => {
mutableInstrumentations[method as string] = createIterableMethod(
method,
false,
false
)
readonlyInstrumentations[method as string] = createIterableMethod(
method,
true,
false
)
shallowInstrumentations[method as string] = createIterableMethod(
method,
false,
true
)
shallowReadonlyInstrumentations[method as string] = createIterableMethod(
method,
true,
true
)
})
return [
mutableInstrumentations,
readonlyInstrumentations,
shallowInstrumentations,
shallowReadonlyInstrumentations
]
}
函數(shù)createInstrumentations分為兩部分,前部分是利用已有的get、set、add、has、clear等等來得到各個(gè)instrumentations,后部分是對各個(gè)instrumentations中的迭代方法的更新。只要不是isReadonly不是真值,則無論是get、set等方法還是keys、values等迭代器接口,都在內(nèi)部進(jìn)行了track或trigger,當(dāng)然,get、has、size等方法 和 幾個(gè)迭代器方法都屬于訪問操作,因此內(nèi)部是使用track來收集依賴,而trigger發(fā)生在增、刪、改操作里,當(dāng)然,也要根據(jù)isReadonly和shallow有所區(qū)分,思路基本和baseHandlers一致。
function get(
target: MapTypes,
key: unknown,
isReadonly = false,
isShallow = false
) {
// #1772: readonly(reactive(Map)) should return readonly + reactive version
// of the value
target = (target as any)[ReactiveFlags.RAW]
const rawTarget = toRaw(target)
const rawKey = toRaw(key)
if (!isReadonly) {
if (key !== rawKey) {
track(rawTarget, TrackOpTypes.GET, key)
}
track(rawTarget, TrackOpTypes.GET, rawKey)
}
const { has } = getProto(rawTarget)
const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
if (has.call(rawTarget, key)) {
return wrap(target.get(key))
} else if (has.call(rawTarget, rawKey)) {
return wrap(target.get(rawKey))
} else if (target !== rawTarget) {
// #3602 readonly(reactive(Map))
// ensure that the nested reactive `Map` can do tracking for itself
target.get(key)
}
}
function has(this: CollectionTypes, key: unknown, isReadonly = false): boolean {
const target = (this as any)[ReactiveFlags.RAW]
const rawTarget = toRaw(target)
const rawKey = toRaw(key)
if (!isReadonly) {
if (key !== rawKey) {
track(rawTarget, TrackOpTypes.HAS, key)
}
track(rawTarget, TrackOpTypes.HAS, rawKey)
}
return key === rawKey
? target.has(key)
: target.has(key) || target.has(rawKey)
}
function size(target: IterableCollections, isReadonly = false) {
target = (target as any)[ReactiveFlags.RAW]
!isReadonly && track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(target, 'size', target)
}
function add(this: SetTypes, value: unknown) {
value = toRaw(value)
const target = toRaw(this)
const proto = getProto(target)
const hadKey = proto.has.call(target, value)
if (!hadKey) {
target.add(value)
trigger(target, TriggerOpTypes.ADD, value, value)
}
return this
}
function set(this: MapTypes, key: unknown, value: unknown) {
value = toRaw(value)
const target = toRaw(this)
const { has, get } = getProto(target)
let hadKey = has.call(target, key)
if (!hadKey) {
key = toRaw(key)
hadKey = has.call(target, key)
} else if (__DEV__) {
checkIdentityKeys(target, has, key)
}
const oldValue = get.call(target, key)
target.set(key, value)
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return this
}
function deleteEntry(this: CollectionTypes, key: unknown) {
const target = toRaw(this)
const { has, get } = getProto(target)
let hadKey = has.call(target, key)
if (!hadKey) {
key = toRaw(key)
hadKey = has.call(target, key)
} else if (__DEV__) {
checkIdentityKeys(target, has, key)
}
const oldValue = get ? get.call(target, key) : undefined
// forward the operation before queueing reactions
const result = target.delete(key)
if (hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
function clear(this: IterableCollections) {
const target = toRaw(this)
const hadItems = target.size !== 0
const oldTarget = __DEV__
? isMap(target)
? new Map(target)
: new Set(target)
: undefined
// forward the operation before queueing reactions
const result = target.clear()
if (hadItems) {
trigger(target, TriggerOpTypes.CLEAR, undefined, undefined, oldTarget)
}
return result
}
1.3 createIterableMethod
這里稍微提一下createIterableMethod,用于利用Map和Set本身的迭代器方法,并做了一點(diǎn)修改,在其中加入了track來收集依賴。
function createIterableMethod(
method: string | symbol,
isReadonly: boolean,
isShallow: boolean
) {
return function (
this: IterableCollections,
...args: unknown[]
): Iterable & Iterator {
const target = (this as any)[ReactiveFlags.RAW]
const rawTarget = toRaw(target)
const targetIsMap = isMap(rawTarget)
const isPair =
method === 'entries' || (method === Symbol.iterator && targetIsMap)
const isKeyOnly = method === 'keys' && targetIsMap
const innerIterator = target[method](...args)
const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
!isReadonly &&
track(
rawTarget,
TrackOpTypes.ITERATE,
isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
)
// return a wrapped iterator which returns observed versions of the
// values emitted from the real iterator
return {
// iterator protocol
next() {
const { value, done } = innerIterator.next()
return done
? { value, done }
: {
value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
done
}
},
// iterable protocol
[Symbol.iterator]() {
return this
}
}
}
}
小結(jié)
分析完各個(gè)部分,可以看到,無論是baseHandlers還是collectionHandlers,思路都是一致的。
但是collectionHandlers只有g(shù)et這一個(gè)代理程序,通過攔截到的key判斷是否是Map和Set實(shí)例自帶的增刪改查的方法,從而返回預(yù)設(shè)好的hack版本的方法或原本的屬性值,然后繼續(xù)后續(xù)的操作。在hack版本的方法里進(jìn)行track和trigger。
以上就是Vue3 源碼分析reactive readonly實(shí)例的詳細(xì)內(nèi)容,更多關(guān)于Vue3 reactive readonly的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決el-tree節(jié)點(diǎn)過濾不顯示下級的問題
這篇文章主要介紹了解決el-tree節(jié)點(diǎn)過濾不顯示下級的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Vue+thinkphp5.1+axios實(shí)現(xiàn)文件上傳
這篇文章主要為大家詳細(xì)介紹了Vue+thinkphp5.1+axios實(shí)現(xiàn)文件上傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
vue?parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽
這篇文章主要介紹了vue?parseHTML函數(shù)源碼解析之析器遇到結(jié)束標(biāo)簽的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07

