基础对象的代理
# 概览
基础对象的代理指的是对基础对象进行代理,使基础对象的属性可以被响应式地访问和修改。该实现具体参见
packages\reactivity\src\baseHandlers.ts
本文主要介绍如下四类基础对象的代理方法:
mutableHandlers:可读写代理readonlyHandlers:只读代理shallowReactiveHandlers:浅响应式代理shallowReadonlyHandlers:浅只读代理
# 源码分析
实际上上述四类代理方法只是实例化了两个类MutableReactiveHandler和ReadonlyReactiveHandler
const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler();
const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler();
const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(true);
const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true);
2
3
4
而MutableReactiveHandler和ReadonlyReactiveHandler都是继承于BaseReactiveHandler类的,BaseReactiveHandler类的主要功能是定义基础的代理方法,而MutableReactiveHandler和ReadonlyReactiveHandler类则是在基础的代理方法上进行了扩展,添加了可读写和只读的功能。
# BaseReactiveHandler类
BaseReactiveHandler类的实现如下:
function hasOwnProperty(key) {
if (!isSymbol(key)) key = String(key);
const obj = toRaw(this);
// 依赖收集
track(obj, "has", key);
return obj.hasOwnProperty(key);
}
class BaseReactiveHandler {
constructor(_isReadonly = false, _isShallow = false) {
this._isReadonly = _isReadonly;
this._isShallow = _isShallow;
}
get(target, key, receiver) {
if (key === "__v_skip") return target["__v_skip"];
const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow;
if (key === "__v_isReactive") {
return !isReadonly2;
} else if (key === "__v_isReadonly") {
return isReadonly2;
} else if (key === "__v_isShallow") {
return isShallow2;
} else if (key === "__v_raw") {
if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype
// this means the reciever is a user proxy of the reactive proxy
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) {
return target;
}
return;
}
const targetIsArray = isArray(target);
if (!isReadonly2) {
let fn;
if (targetIsArray && (fn = arrayInstrumentations[key])) {
return fn;
}
if (key === "hasOwnProperty") {
return hasOwnProperty;
}
}
const res = Reflect.get(target, key, isRef(target) ? target : receiver);
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res;
}
if (!isReadonly2) {
track(target, "get", key);
}
if (isShallow2) {
return res;
}
if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value;
}
if (isObject(res)) {
return isReadonly2 ? readonly(res) : reactive(res);
}
return res;
}
}
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
BaseReactiveHandler类只是定义了get()方法,当读取代理对象时,会触发get()方法进行拦截,在非只读的情况下,会调用track方法进行依赖收集。如下是BaseReactiveHandler类的实现过程分析:
- 类的构造器接收两个参数:
_isReadonly和_isShallow,分别表示是否只读和是否是浅层响应式. get()方法接收三个参数:target目标对象、key属性名、receiver代理对象。当get()方法被触发时,若key是__v_isReactive、__v_isShallow或者__v_raw,则根据实例的_isReadonly或者_isShallow返回对应的布尔值.- 若
key是__v_raw,则根据_isReadonly和_isShallow确定代理对象的缓存(即调用createReactiveObject中的proxyMap缓存变量),然后比较receiver是否是缓存中的代理对象,如果是,则返回目标对象target;若不是,则判断target和receiver的原型是否相等,若相等,则返回目标对象target.如果二者皆不相等,则什么也不返回. - 若
key不是上述属性,则判断target是否是数组。如果是可写的,且target是数组,key又是数组的属性,那么就返回数组的方法。若key是hasOwnProperty,则返回自定义的hasOwnProperty方法,该方法的实现如上,该方法的作用是判断target是否有key属性,并且调用track进行依赖收集. - 调用
Reflect.get(target, key, receiver)方法获取target上key的值res. - 判断
key是不是内置的Symbol属性或一些普通属性__proto__,__v_isRef,__isVue,若是,则直接返回res,避免对这些属性进行依赖收集. - 判断是否只读,若不是只读,则调用
track进行依赖收集. - 判断是否是浅层响应式,若是,则直接返回
res,避免对res进行递归代理. - 判断
res是否是Ref对象,若是,则继续判断,若target是数组且key是整数索引,则返回res;否则返回res.value. - 若
res是对象,则判断是否只读,若不是只读,则递归调用reactive进行代理,否则递归调用readonly进行代理. - 最后返回
res.
# MutableReactiveHandler类
MutableReactiveHandler的实现如下:
class MutableReactiveHandler extends BaseReactiveHandler {
constructor(isShallow2 = false) {
super(false, isShallow2);
}
set(target, key, value, receiver) {
let oldValue = target[key];
if (!this._isShallow) {
const isOldValueReadonly = isReadonly(oldValue);
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue);
value = toRaw(value);
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
if (isOldValueReadonly) {
return false;
} else {
oldValue.value = value;
return true;
}
}
}
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, "add", key, value);
} else if (hasChanged(value, oldValue)) {
trigger(target, "set", key, value, oldValue);
}
}
return result;
}
deleteProperty(target, key) {
const hadKey = hasOwn(target, key);
const oldValue = target[key];
const result = Reflect.deleteProperty(target, key);
if (result && hadKey) {
trigger(target, "delete", key, void 0, oldValue);
}
return result;
}
has(target, key) {
const result = Reflect.has(target, key);
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, "has", key);
}
return result;
}
ownKeys(target) {
track(
target,
"iterate",
isArray(target) ? "length" : ITERATE_KEY
);
return Reflect.ownKeys(target);
}
}
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
MutableReactiveHandler类继承于BaseReactiveHandler类,在其内部定义了set、deleteProperty、has、ownKeys方法四个方法。在它的构造函数中,接受一个参数isShallow2表示是否是浅层响应,默认为false,然后调用super,响应式对象肯定不是只读的,所以super第一个参数是false。如下分析MutableReactiveHandler的四个方法。
# set
当target目标对象上的值发生改变,或者说是对target进行写操作时,会调用set方法。
set方法接收四个参数:target目标对象、key属性名、value属性值、receiver代理对象。
- 首先获取
target的旧值oldValue,判断是否是浅层代理,若不是,则继续判断新值,若新值是深层响应式,则分别调用toRaw获取新值和旧值的原始值;然后判断,若target是对象,且旧值是Ref对象而新值不是Ref对象,若旧值是只读的,则返回false;否则将旧值的value属性赋值为新值,返回true. - 若
target不是数组,且key是整数索引且key的长度小于target数组的长度,则说明是在更新target[key]的值;若target不是数组,则调用自定义方法hasOwn(实际就是Object.prototype.hasOwnProperty)判断target是否有key属性.hadKey用于表示是更新还是新增操作,true则更新/false则新增. - 调用
Reflect.set设置target的key为value,返回结果保存至变量result。 - 判断
target和receiver的原始对象是否相等,只有相等才触发副作用。这个检查是为了避免在原型链上的属性设置时错误地触发副作用。然后判断hadKey,若hadKey为true,则说明是更新操作,会先调用hasChanged判断新旧值是否相等,若不等,则调用trigger触发更新副作用;若hadKey为false,则说明是新增操作,调用trigger触发新增副作用. - 最后返回
result
# deleteProperty
deleteProperty方法在删除target上的某属性时会被触发。
deleteProperty方法接收两个参数:target目标对象、key属性名。
- 调用
hasOwn判断target是否有key属性,判断结果记为hadKey,获取旧值oldValue。 - 调用
Reflect.deleteProperty删除target的key属性,返回结果保存至变量result。 - 判断
result与hadKey都为true,则调用trigger触发删除副作用。 - 最后返回
result
# has
has方法会拦截in操作符。如下是它的处理流程:
- 调用
Reflect.has判断target是否有key属性,记为result - 判断
key是否是Symbol类型或者key是否是内置的Symbol,若不是,则调用track进行依赖收集. - 最后返回属性检测的结果
result
# ownKeys
ownKeys方法会拦截Object.keys、Object.getOwnPropertyNames、Object.getOwnPropertySymbols、for...in循环。如下是它的处理流程:
- 调用
track进行依赖收集。若target是数组,则跟踪length属性的变化;若target不是数组,则跟踪Symbol(iterator)。这样当对对象的属性添加、删除或数组长度变化时,就会触发相关的副作用函数。 - 最后返回目标对象的所有自有属性键
# ReadonlyReactiveHandler类
ReadonlyReactiveHandler类就更简单了,因为通过该类实例化的处理器方法都是针对只读对象的,所以它的set、deleteProperty方法都直接返回true,并在warn中提示目标对象是只读的。
ReadonlyReactiveHandler的实现如下:
class ReadonlyReactiveHandler extends BaseReactiveHandler {
constructor(isShallow2 = false) {
super(true, isShallow2);
}
set(target, key) {
{
warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
);
}
return true;
}
deleteProperty(target, key) {
{
warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
);
}
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23