上次一起阅读了watch
和computed
的源码,其实应该先看副作用effect
,因为各个响应式的API
里基本都用到了,等结束了reactive
和readonly
和ref
,就一起看看effect
。这次要说的是reactive
和readonly
,两者在实现上流程大体一致。尤其是对Map
和Set
的方法的代理拦截,多少有点妙。
一、reactive
和 readonly
Vue3
使用Proxy
来替代Vue2
中Object.defineProperty
。
1 2 3 4 5 6 7 8 9 10 11 12 13 const target = { name : "onlyy~" , }; const proxy = new Proxy (target, { get (target, property, receiver ) { return Reflect .get (target, property, receiver); }, });
1. reactive
相关类型 reactive
利用Proxy
来定义一个响应式对象。
Target
:目标对象,包含几个标志,以及__v_raw
字段,该字段表示它原本的非响应式状态的值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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. 相关全局变量与方法
ReactiveFlags
:定义了各种标志对应的字符串(作为reactive
对象的属性)的枚举;
reactiveMap
shallowReactiveMap
readonlyMap
shallowReadonlyMap
:这几个Map
分别用于存放对应API
生成的响应式对象(以目标对象为key
,代理对象为value
),便于后续判断某个对象是否存在已创建的响应式对象;
TargetType
:枚举成员的内容分别用于区分代理目标是否校验合法、普通对象、Set
或Map
;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 , }
然后是两个函数:targetTypeMap
用于判断各种JS
类型属于TargetType
中的哪种;getTargetType
用于获取target
对应的TargetType
类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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
函数 reactive
入参类型为object
,返回值类型是UnwrapNestedRefs
,对嵌套的Ref
进行了解包。意味着即使reactive
接收一个Ref
,其返回值也不用再像Ref
那样通过.value
来读取值。源码的注释中也给出了示例。
reactive
内部调用createReactiveObject
来创建响应式对象。瞄一眼入参有五个:
target
:代理目标;
false
:对应createReactiveObject
的isReadonly
参数;
mutableHandlers
:普通对象和数组的代理处理程序;
mutableCollectionHandlers
:Set
和Map
的代理处理程序;
reactiveMap
:之前定义的全局变量,收集reactive
对应的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export function reactive<T extends object >(target : T): UnwrapNestedRefs <T>;export function reactive (target: object ) { if (isReadonly (target)) { return target; } return createReactiveObject ( target, false , mutableHandlers, mutableCollectionHandlers, reactiveMap ); }
4. 造物主createReactiveObject
不论是reactive
,还是shallowReactive
、readonly
和shallowReadonly
,都是内部调用createReactiveObject
来创建代理的。createReactiveObject
也没什么操作,主要判断了下target
的类型,再决定是直接返回target
还是返回一个新建的proxy
。
以下情况直接返回target
:
target
不是对象;
target
已经是一个响应式的对象,即由createReactiveObject
创建的proxy
;
target
类型校验不合法,例如RegExp
、Date
等;
当参数proxyMap
对应的实参(可能为reactiveMap
、shallowReactiveMap
、readonlyMap
或shallowReadonlyMap
,分别对应ractive
、shallowReactive
、readonly
和shallowReadonly
四个API
)里已经存在了target
的响应式对象时,直接取出并返回该响应式对象;
否则,创建一个target
的响应式对象proxy
,将proxy
加入到proxyMap
中,然后返回该proxy
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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; } if ( target[ReactiveFlags .RAW ] && !(isReadonly && target[ReactiveFlags .IS_REACTIVE ]) ) { return target; } const existingProxy = proxyMap.get (target); if (existingProxy) { return existingProxy; } 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; }
我们知道,代理的重点其实在与代理的处理程序,createReactiveObject
根据普通对象和数组类型、Set
和Map
类型来区分baseHandlers
和collectionHandlers
。
5. shallowReactive
、readonly
和shallowReadonly
事实上,ractive
、shallowReactive
、readonly
和shallowReadonly
这几个函数形式上基本一致,都是通过createReactiveObject
来创建响应式对象,存储在对应的proxyMap
里,但是对应的baseHandlers
和collectionHandlers
有区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 export function shallowReactive<T extends object >( target : T ): ShallowReactive <T> { return createReactiveObject ( target, false , shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap ); } export function readonly <T extends object >( target : T ): DeepReadonly <UnwrapNestedRefs <T>> { return createReactiveObject ( target, true , readonlyHandlers, readonlyCollectionHandlers, readonlyMap ); } export function shallowReadonly<T extends object >(target : T): Readonly <T> { return createReactiveObject ( target, true , shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap ); }
事实上,ractive
、shallowReactive
、readonly
和shallowReadonly
这几个函数形式上基本一致,都是通过createReactiveObject
来创建响应式对象,存储在对应的proxyMap
里,但是对应的baseHandlers
和collectionHandlers
有区别。那么我们就知道了,其实重点都在各种handlers
里。
二、对应的 Handlers
baseHandlers
用于普通对象和数组的代理,collectionHandlers
用于Set
、Map
等的代理。对应ractive
、shallowReactive
、readonly
和shallowReadonly
四个API
,每一个都有自己的baseHandlers
和collectionHandlers
。
1. baseHandlers
在packages/reactivity/src/baseHandlers.ts
文件中。分别导出了这 4 个API
对应的baseHandlers
。
1.1 reactive
reactive
的baseHandlers
中有 5 个代理程序。
1 2 3 4 5 6 7 8 export const mutableHandlers : ProxyHandler <object > = { get, set, deleteProperty, has, ownKeys, };
在拦截过程中,在get
、has
和ownKey
这几个访问程序中进行依赖捕获(track
),在set
和deleteProperty
这俩用于更改的程序中触发更新(trigger
) 。
get
和set
分别由函数createGetter
和createSetter
创建,这俩函数根据入参的不同,返回不同的get
和set
,readonly
等API
的baseHandlers
中的get
和set
也大都源于此,除了两种readonly
中用于告警的set
。
(1) get
createGetter
两个入参:isReadonly
和isShallow
,两两组合正好对应四个API
。
shallow
:为true
时不会进入递归环节,因此是浅层的处理;
isReadonly
:在createGetter
中影响proxyMap
的选择和递归时API
的选择,它主要发挥作用是在set
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 function createGetter (isReadonly = false , shallow = false ) { return function get (target: Target, key: string | symbol , receiver: object ) { if (key === ReactiveFlags .IS_REACTIVE ) { return !isReadonly; } else if (key === ReactiveFlags .IS_READONLY ) { return isReadonly; } else if (key === ReactiveFlags .IS_SHALLOW ) { return shallow; } else if ( key === ReactiveFlags .RAW && receiver === (isReadonly ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap ).get (target) ) { return target; } const targetIsArray = isArray (target); 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; } if (!isReadonly) { track (target, TrackOpTypes .GET , key); } if (shallow) { return res; } if (isRef (res)) { return targetIsArray && isIntegerKey (key) ? res : res.value ; } if (isObject (res)) { return isReadonly ? readonly (res) : reactive (res); } return res; }; }
(2) set
对于reactive
,可以说最主要的任务就是在set
中触发更新,set
包括 新增 和 修改 属性值。如果当前的key
对应的值是一个Ref
,且其它条件满足时,则触发更新的操作是在Ref
的内部。这些在后续讲解Ref
的时候会提到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 function createSetter (shallow = false ) { return function set ( target: object , key: string | symbol , value: unknown , receiver: object ): boolean { let oldValue = (target as any )[key]; if (isReadonly (oldValue) && isRef (oldValue) && !isRef (value)) { return false ; } if (!shallow) { if (!isShallow (value) && !isReadonly (value)) { oldValue = toRaw (oldValue); value = toRaw (value); } if (!isArray (target) && isRef (oldValue) && !isRef (value)) { oldValue.value = value; return true ; } } else { } const hadKey = isArray (target) && isIntegerKey (key) ? Number (key) < target.length : hasOwn (target, key); const result = Reflect .set (target, key, value, receiver); if (target === toRaw (receiver)) { if (!hadKey) { trigger (target, TriggerOpTypes .ADD , key, value); } else if (hasChanged (value, oldValue)) { trigger (target, TriggerOpTypes .SET , key, value, oldValue); } } return result; }; }
(3) deleteProperty
删除操作的代理程序,和set
一样,deleteProperty
拦截delete
和Reflect.deleteProperty()
操作,它也能触发更新。
1 2 3 4 5 6 7 8 9 10 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); if (result && hadKey) { trigger (target, TriggerOpTypes .DELETE , key, undefined , oldValue); } return result; }
(4) has
has
用于判断target
中是否有当前的key
,拦截a in obj
、with(obj){(a)}
、Reflect.has
等操作,属于访问程序,在其中进行has
操作的依赖收集。
1 2 3 4 5 6 7 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
,属于访问程序,在其中进行迭代的依赖收集。
1 2 3 4 function ownKeys (target: object ): (string | symbol )[] { track (target, TrackOpTypes .ITERATE , isArray (target) ? "length" : ITERATE_KEY ); return Reflect .ownKeys (target); }
现在我们算是都弄明白了,对于普通对象和数组,reactive
创建proxy
,通过get
、set
、deleteProperty
、has
、ownKeys
五个代理处理程序,来拦截其属性访问操作,在其中进行依赖收集,拦截其增删改操作,其中触发更新。
1.2 readonly
readonly
的代理处理程序只有三个:
get
:由createGetter(true)
创建,还记得我们上面讲到的createSetter
吗?
set
deleteProperty
:这两个代理处理程序用于告警,毕竟readonly
不可修改。
毕加思索一下createGetter(true)
,传入的readonly=true
,使得get
中不会进行track
操作来收集依赖,因而不具有响应性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const readonlyGet = 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
。具体实现也可以回顾上面说到的createGetter
和createSetter
。
回过头来看看createGetter(false, true)
,isReadonly = false
,则在get
中,可以进行track
依赖收集;shallow = true
,则在get
中不会对顶层的Ref
进行解包,也不会进行递归操作。
而在createSetter(true)
中,参数shallow
几乎只影响是否要解出原本的raw
值。如果新值value
不是浅层且不是只读的,则需要解出它的原本raw
值,之后才能进行赋值操作,否则我们的shallowRef
将不再是浅层的了。
1 2 3 4 5 6 7 8 9 10 11 const shallowGet = createGetter (false , true );const shallowSet = createSetter (true );export const shallowReactiveHandlers = extend ( {}, mutableHandlers, { get : shallowGet, set : shallowSet, } );
1.4 shallowReadonly
移植了readonly
的baseHandlers
,更新了其中的get
,这个get
也试试由createGetter
创建。我们知道,readonly
的baseHandlers
里,除了get
,另外俩都是用来拦截修改操作并告警的。
回顾一下createGetter
,当isReadonly===true
时,不会进行track
操作来收集依赖;shallow===true
时,不会对Ref
进行解包,也不会走到递归环节,即是浅层的readonly
。
1 2 3 4 5 6 7 8 9 10 11 12 const shallowReadonlyGet = createGetter (true , true );export const shallowReadonlyHandlers = extend ( {}, readonlyHandlers, { get : shallowReadonlyGet, } );
2. cellectionHandlers
对于Set
和Map
较为复杂的数据结构,他们有自己的方法,因此代理程序会有些差别。基本都是拦截它们原本的方法,然后进行track
或trigger
。可以看到这几个handlers
中,都只有由createInstrumentationGetter
创建的get
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export const mutableCollectionHandlers : ProxyHandler <CollectionTypes > = { get : createInstrumentationGetter (false , false ), }; export const shallowCollectionHandlers : ProxyHandler <CollectionTypes > = { get : createInstrumentationGetter (false , true ), }; export const readonlyCollectionHandlers : ProxyHandler <CollectionTypes > = { get : createInstrumentationGetter (true , false ), }; export const shallowReadonlyCollectionHandlers : ProxyHandler <CollectionTypes > = { get : createInstrumentationGetter (true , true ), };
1.1 createInstrumentationGetter
因为是代理Set
和Map
,在拦截它们的实例方法之前,对实例的访问,即get
,这个get
并非Map
或Set
实例的get
方法,而是表示对实例的访问操作。例如:const map = new Map([['name', 'cc']]); map.set('age', 18);
。这里map.set()
首先就是访问map
的set
方法,对应的key
就是字符串'set'
,而这一步就会被代理的get
程序拦截,而真正的对方法的拦截,都在相应的instrumentations
里预设好了。拦截了之后,如果key
在instrumentations
里存在,返回预设的方法,在其中进行track
和trigger
操作,否则是其它属性/方法,直接返回即可,不会进行track
和trigger
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const [ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations, ] = 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
的方法的调用,而是通过get
程序来拦截,再判断key
是否为执行增删改查的方法,从而判断是否进行依赖收集或更新。因此,就需要先预设好,哪些key
作为方法名时可以触发track
和trigger
。其实也就是Map
和Set
的那些实例方法和迭代器方法。而各种Instrumentations
,就是这些预设的方法,track
和trigger
操作都在其中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 function createInstrumentations ( ) { 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 ), }; 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 ), }; 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 ), }; 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 ), }; 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, ]; }
函数createInstrumentations
分为两部分,前部分是利用已有的get
、set
、add
、has
、clear
等等来得到各个instrumentations
,后部分是对各个instrumentations
中的迭代方法的更新。只要不是isReadonly
不是真值,则无论是get
、set
等方法还是keys
、values
等迭代器接口,都在内部进行了track
或trigger
,当然,get
、has
、size
等方法 和 几个迭代器方法都属于访问操作,因此内部是使用track
来收集依赖,而trigger
发生在增、删、改操作里,当然,也要根据isReadonly
和shallow
有所区分,思路基本和baseHandlers
一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 function get ( target: MapTypes, key: unknown , isReadonly = false , isShallow = false ) { 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) { 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 ; 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 ; const result = target.clear (); if (hadItems) { trigger (target, TriggerOpTypes .CLEAR , undefined , undefined , oldTarget); } return result; }
1.3 createIterableMethod
这里稍微提一下createIterableMethod
,用于利用Map
和Set
本身的迭代器方法,并做了一点修改,在其中加入了track
来收集依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 { next ( ) { const { value, done } = innerIterator.next (); return done ? { value, done } : { value : isPair ? [wrap (value[0 ]), wrap (value[1 ])] : wrap (value), done, }; }, [Symbol .iterator ]() { return this ; }, }; }; }
1.4 小结 分析完各个部分,可以看到,无论是baseHandlers
还是collectionHandlers
,思路都是一致的。
但是collectionHandlers
只有get
这一个代理程序,通过拦截到的key
判断是否是Map
和Set
实例自带的增删改查的方法,从而返回预设好的hack
版本的方法或原本的属性值,然后继续后续的操作。在hack
版本的方法里进行track
和trigger
。