reactive响应式依赖的收集与触发监听
# 概览
在BaseReactiveHandler
类的get
方法中,有如下代码块if (!isReadonly2){track(target, "get", key);}
,这表示通过reactive
、shallowReactive
创建的响应式对象,非只读的,当读取代理对象proxyTarget
的某个属性key
时,都会被该get
方法拦截,即调用track()
方法建立依赖。
而当对代理对象proxyTarget
进行赋值或更新某个属性的值时,会被set
方法拦截,即调用trigger()
方法触发依赖(而删除会被deleteProperty
拦截)。
因此对于reactive
响应式对象的响应式处理,和track
与trigger
方法密不可分。
本文主要介绍track
与trigger
是如何进行依赖的收集与触发的全流程。
# 源码分析
在vue3中,维护了一个全局的targetMap
WeakMap
实例对象,用于存储响应式对象与依赖的映射关系。
const targetMap = new WeakMap();
# track
方法
track
方法收集依赖就是往变量targetMap
中添加相关元素,存储响应式对象与依赖的映射关系。
track
的源码实现如下:
function track(target, type, key) {
if (shouldTrack && activeSub) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, depsMap = new Map());
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, dep = new Dep());
dep.map = depsMap;
dep.key = key;
}
dep.track();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这里暂且不论shouldTrack
和activeSub
,假定满足track
的条件。首先从targetMap
中读取depsMap
,若depsMap
中不存在,则创建一个新的Map
的实例,并将其赋值给depsMap
,并且存储到targetMap
中。然后从depsMap
中获取key
对应的依赖关系dep
,同理,若dep
不存在,则创建一个新的Dep
实例,并将其赋值给dep
,并且存储到depsMap
中,然后将dep
的map
和key
分别绑定depsMap
、key
。最后调用dep.track()
方法。
dep.track()
执行后,会将当前的activeSub
添加到dep
的subs
数组中?。
# trigger
方法
track
方法的源码实现如下:
function trigger(target, type, key, newValue, oldValue, oldTarget) {
const depsMap = targetMap.get(target);
if (!depsMap) {
globalVersion++;
return;
}
const run = (dep) => {
if (dep) {
dep.trigger();
}
};
startBatch();
if (type === "clear") {
depsMap.forEach(run);
} else {
const targetIsArray = isArray(target);
const isArrayIndex = targetIsArray && isIntegerKey(key);
if (targetIsArray && key === "length") {
const newLength = Number(newValue);
depsMap.forEach((dep, key2) => {
if (key2 === "length" || key2 === ARRAY_ITERATE_KEY || !isSymbol(key2) && key2 >= newLength) {
run(dep);
}
});
} else {
if (key !== void 0 || depsMap.has(void 0)) {
run(depsMap.get(key));
}
if (isArrayIndex) {
run(depsMap.get(ARRAY_ITERATE_KEY));
}
switch (type) {
case "add":
if (!targetIsArray) {
run(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
run(depsMap.get(MAP_KEY_ITERATE_KEY));
}
} else if (isArrayIndex) {
run(depsMap.get("length"));
}
break;
case "delete":
if (!targetIsArray) {
run(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
run(depsMap.get(MAP_KEY_ITERATE_KEY));
}
}
break;
case "set":
if (isMap(target)) {
run(depsMap.get(ITERATE_KEY));
}
break;
}
}
}
endBatch();
}
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