概览
基础对象的代理指的是对基础对象进行代理,使基础对象的属性可以被响应式地访问和修改。该实现具体参见
packages\reactivity\src\baseHandlers.ts
本文主要介绍如下四类基础对象的代理方法:
mutableHandlers
:可读写代理readonlyHandlers
:只读代理shallowReactiveHandlers
:浅响应式代理shallowReadonlyHandlers
:浅只读代理
源码分析
实际上上述四类代理方法只是实例化了两个类MutableReactiveHandler
和ReadonlyReactiveHandler
js
const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler();
const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler();
const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(true);
const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true);
而MutableReactiveHandler
和ReadonlyReactiveHandler
都是继承于BaseReactiveHandler
类的,BaseReactiveHandler
类的主要功能是定义基础的代理方法,而MutableReactiveHandler
和ReadonlyReactiveHandler
类则是在基础的代理方法上进行了扩展,添加了可读写和只读的功能。
BaseReactiveHandler
类
BaseReactiveHandler
类的实现如下:
js
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;
}
}
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
的实现如下:
js
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);
}
}
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
的实现如下:
js
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;
}
}