上次一起阅读了watchcomputed的源码,其实应该先看副作用effect,因为各个响应式的API里基本都用到了,等结束了reactivereadonlyref,就一起看看effect。这次要说的是reactivereadonly,两者在实现上流程大体一致。尤其是对MapSet的方法的代理拦截,多少有点妙。

一、reactivereadonly

Vue3使用Proxy来替代Vue2Object.defineProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
const target = {
name: "onlyy~",
};

// 创建一个对target的代理
const proxy = new Proxy(target, {
// ...各种handler,例如get,set...
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:枚举成员的内容分别用于区分代理目标是否校验合法、普通对象、SetMap
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来读取值。源码的注释中也给出了示例。

1
2
3
4
5
6
7
8
9
10
/*
* const count = ref(0)
* const obj = reactive({
* count
* })
*
* obj.count++
* obj.count // -> 1
* count.value // -> 1
*/

reactive内部调用createReactiveObject来创建响应式对象。瞄一眼入参有五个:

  • target:代理目标;
  • false:对应createReactiveObjectisReadonly参数;
  • mutableHandlers:普通对象和数组的代理处理程序;
  • mutableCollectionHandlersSetMap的代理处理程序;
  • 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 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,还是shallowReactivereadonlyshallowReadonly,都是内部调用createReactiveObject来创建代理的。createReactiveObject也没什么操作,主要判断了下target的类型,再决定是直接返回target还是返回一个新建的proxy

以下情况直接返回target

  • target不是对象;
  • target已经是一个响应式的对象,即由createReactiveObject创建的proxy
  • target类型校验不合法,例如RegExpDate等;

当参数proxyMap对应的实参(可能为reactiveMapshallowReactiveMapreadonlyMapshallowReadonlyMap,分别对应ractiveshallowReactivereadonlyshallowReadonly四个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;
}
// 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;
}

我们知道,代理的重点其实在与代理的处理程序,createReactiveObject根据普通对象和数组类型、SetMap类型来区分baseHandlerscollectionHandlers

5. shallowReactivereadonlyshallowReadonly

事实上,ractiveshallowReactivereadonlyshallowReadonly这几个函数形式上基本一致,都是通过createReactiveObject来创建响应式对象,存储在对应的proxyMap里,但是对应的baseHandlerscollectionHandlers有区别。

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
// shallowReactive
export function shallowReactive<T extends object>(
target: T
): ShallowReactive<T> {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap
);
}

// raedonly
// 注意readonly不是响应式的,而是一个原对象的只读的拷贝
// 具体实现在对应的handlers里
export function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers,
readonlyMap
);
}

// shallowReadonly
// 是响应式的
// 只有最外层是只读的
export function shallowReadonly<T extends object>(target: T): Readonly<T> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers,
shallowReadonlyMap
);
}

事实上,ractiveshallowReactivereadonlyshallowReadonly这几个函数形式上基本一致,都是通过createReactiveObject来创建响应式对象,存储在对应的proxyMap里,但是对应的baseHandlerscollectionHandlers有区别。那么我们就知道了,其实重点都在各种handlers里。

二、对应的 Handlers

baseHandlers用于普通对象和数组的代理,collectionHandlers用于SetMap等的代理。对应ractiveshallowReactivereadonlyshallowReadonly四个API,每一个都有自己的baseHandlerscollectionHandlers

1. baseHandlers

packages/reactivity/src/baseHandlers.ts文件中。分别导出了这 4 个API对应的baseHandlers

1.1 reactive

reactivebaseHandlers中有 5 个代理程序。

1
2
3
4
5
6
7
8
// reactive
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys,
};

在拦截过程中,在gethasownKey这几个访问程序中进行依赖捕获(track),在setdeleteProperty这俩用于更改的程序中触发更新(trigger) 。

getset分别由函数createGettercreateSetter创建,这俩函数根据入参的不同,返回不同的getsetreadonlyAPIbaseHandlers中的getset也大都源于此,除了两种readonly中用于告警的set

(1) get

createGetter两个入参:isReadonlyisShallow,两两组合正好对应四个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分支判断target是否已经是由这几个API创建的代理对象,代理得到的proxy才具有这些key
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 (
// __v_raw 属性对应 代理对象的目标对象
// 当该属性有值,且在相应的proxyMap中存在代理对象时,说明target已经是一个proxy了
// __v_raw 属性对应的值为target本身
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target;
}

const targetIsArray = isArray(target);

// 对数组的几个方法进行代理,在'includes', 'indexOf', 'lastIndexOf'等方法中进行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 为非响应式的
if (!isReadonly) {
track(target, TrackOpTypes.GET, key);
}

if (shallow) {
return res;
}

// 如果get到的值是一个Ref,会直接解包,无需再使用 .value 来获取真正需要的值
// 除非目标对象target是数组,或者当前的key是整数
// 例如,obj[0],即使是一个Ref也不会直接解包,使用的时候依然要 obj[0].value
// shallow没有走到这一步,因此也不会自动解包
if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value;
}

// 当get到的值是对象时,根据是否是readonly来递归操作,需要防止对象循环引用
// 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,可以说最主要的任务就是在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];
// 当前值是Readonly的Ref,而新值不是Ref时,不允许修改
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false;
}
// 如果是深层的修改
if (!shallow) {
// 解出原本的非proxy值
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue);
value = toRaw(value);
}
// 目标对象非数组,当前key的值是Ref而新值不是Ref,则通过 .value 赋值
// 在Ref内部触发更新
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value;
return true;
}
} else {
// 浅层模式下,忽略对象是否是响应式的
// in shallow mode, objects are set as-is regardless of reactive or not
}

// 然后是触发更新的部分了

// 判断当前key是否已经存在于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
// 如果是原型链上的字段则不会触发更新
if (target === toRaw(receiver)) {
if (!hadKey) {
// 当前的key已经存在,触发新增的更新
trigger(target, TriggerOpTypes.ADD, key, value);
} else if (hasChanged(value, oldValue)) {
// 当前key不存在,触发修改的更新
trigger(target, TriggerOpTypes.SET, key, value, oldValue);
}
}
return result;
};
}
(3) deleteProperty

删除操作的代理程序,和set一样,deleteProperty拦截deleteReflect.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);
// 删除成功 且 target中原来有这个属性时,触发删除的更新
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue);
}
return result;
}
(4) has

has用于判断target中是否有当前的key,拦截a in objwith(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.getOwnPropertyNamesObject.getOwnPropertySymbolsObject.keysReflect.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,通过getsetdeletePropertyhasownKeys五个代理处理程序,来拦截其属性访问操作,在其中进行依赖收集,拦截其增删改操作,其中触发更新。

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 = /*#__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移植了reactivebaseHandlers,并且更新了getset。具体实现也可以回顾上面说到的createGettercreateSetter

回过头来看看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 = /*#__PURE__*/ createGetter(false, true);
const shallowSet = /*#__PURE__*/ createSetter(true);

export const shallowReactiveHandlers = /*#__PURE__*/ extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet,
}
);

1.4 shallowReadonly

移植了readonlybaseHandlers,更新了其中的get,这个get也试试由createGetter创建。我们知道,readonlybaseHandlers里,除了get,另外俩都是用来拦截修改操作并告警的。

回顾一下createGetter,当isReadonly===true时,不会进行track操作来收集依赖;shallow===true时,不会对Ref进行解包,也不会走到递归环节,即是浅层的readonly

1
2
3
4
5
6
7
8
9
10
11
12
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

对于SetMap较为复杂的数据结构,他们有自己的方法,因此代理程序会有些差别。基本都是拦截它们原本的方法,然后进行tracktrigger。可以看到这几个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: /*#__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

因为是代理SetMap,在拦截它们的实例方法之前,对实例的访问,即get,这个get并非MapSet实例的get方法,而是表示对实例的访问操作。例如:const map = new Map([['name', 'cc']]); map.set('age', 18);。这里map.set()首先就是访问mapset方法,对应的key就是字符串'set',而这一步就会被代理的get程序拦截,而真正的对方法的拦截,都在相应的instrumentations里预设好了。拦截了之后,如果keyinstrumentations里存在,返回预设的方法,在其中进行tracktrigger操作,否则是其它属性/方法,直接返回即可,不会进行tracktrigger

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,
] = /* #__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无法直接拦截MapSet的方法的调用,而是通过get程序来拦截,再判断key是否为执行增删改查的方法,从而判断是否进行依赖收集或更新。因此,就需要先预设好,哪些key作为方法名时可以触发tracktrigger。其实也就是MapSet的那些实例方法和迭代器方法。而各种Instrumentations,就是这些预设的方法,tracktrigger操作都在其中。

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() {
// 对应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),
};

// 对应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),
};

// 对应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),
};

// 对应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 挂上几个迭代器
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分为两部分,前部分是利用已有的getsetaddhasclear等等来得到各个instrumentations,后部分是对各个instrumentations中的迭代方法的更新。只要不是isReadonly不是真值,则无论是getset等方法还是keysvalues等迭代器接口,都在内部进行了tracktrigger,当然,gethassize等方法 和 几个迭代器方法都属于访问操作,因此内部是使用track来收集依赖,而trigger发生在增、删、改操作里,当然,也要根据isReadonlyshallow有所区分,思路基本和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
) {
// #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,用于利用MapSet本身的迭代器方法,并做了一点修改,在其中加入了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 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;
},
};
};
}

1.4 小结

分析完各个部分,可以看到,无论是baseHandlers还是collectionHandlers,思路都是一致的。

但是collectionHandlers只有get这一个代理程序,通过拦截到的key判断是否是MapSet实例自带的增删改查的方法,从而返回预设好的hack版本的方法或原本的属性值,然后继续后续的操作。在hack版本的方法里进行tracktrigger