mutableHandlers
mutableHandlers
是非集合对象的普通响应式代理处理函数,来看看代码:
export const mutableHandlers: ProxyHandler<object> = {
get, // 用于拦截对象的读取属性操作
set, // 用于拦截对象的设置属性操作
deleteProperty, // 用于拦截对象的删除属性操作
has, // 检查一个对象是否拥有某个属性
ownKeys // 针对 getOwnPropertyNames, getOwnPropertySymbols, keys 的代理方法
}
Set handler
set
拦截器方法通过 createSetter
创建,这个函数首先判断是否为浅层代理模式,深层代理需要将嵌套的对象也转化为响应式代理,浅层则保持原值:
- 如果是深层代理则调用
toRaw
获取value
和oldValue
的原始值,便于在后序的trigger
操作中使用。
之后 set
拦截器中主要执行的就是 trigger
操作,触发属性改变带来的副作用。对于触发 trigger
操作的类型 vue
做了如下的判断:
- 对象原本就包含此属性,并且对于旧值来说
hasChanged
,则触发TriggerOpTypes.SET
; - 如果对象本不包含此属性,则触发
TriggerOpTypes.ADD
;
const set = /*#__PURE__*/ createSetter()
/**
* @description: 拦截对象的设置属性操作
* @param {shallow} 是否是浅观察
*/
function createSetter(shallow = false) {
/**
* @description:
* @param {target} 目标对象
* @param {key} 设置的属性的名称
* @param {value} 要改变的属性值
* @param {receiver} 如果遇到 setter,receiver则为setter调用时的this值
*/
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
const oldValue = (target as any)[key]
// 如果模式不是浅观察
if (!shallow) {
value = toRaw(value)
oldValue = toRaw(oldValue)
// 并且目标对象不是数组,旧值是ref,新值不是ref,则直接旧的 oldRef.value 赋值
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
}
// 检查对象是否有这个属性并赋值
const hadKey = 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) {
// 如是不存在则trigger ADD
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 存在则trigger SET
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
toRaw
重点讲一下 toRaw
方法,toRaw
用于返回对象的原始值,如果 observed
是一个响应式代理对象,那就通过 ReactiveFlags.RAW
拿到其原始值,否则返回其本身:
export function toRaw<T>(observed: T): T {
return (
(observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
// a && b; if both a and b are truthy, it returns b;
// a || b; if only b is truthy, it returns b;
)
}
Get Handler
Get
拦截器的创建方式和 set
相同,函数流程如下:
如果|_GET_|
操作获取的是 ReactiveFlags.IS_REACTIVE|IS_READONLY|RAW
,处理逻辑为:
ReactiveFlags.IS_REACTIVE
:!isReadonly
返回响应式代理是否为普通响应式代理。ReactiveFlags.IS_READONLY
:返回响应式代理是否为只读响应式代理。RAW
:返回响应式代理的原始值,注意target
源对象就是在这里被缓存的。
之后如果目标对象是数组并且 key
属于三个方法之一 ['includes', 'indexOf', 'lastIndexOf']
,走 arrayInstrumentations
的处理流程。
排除掉属性为内置方法|属性,原型对象方法|属性之后,如果不是只读代理那就触发 track
操作记录副作用。
最后判断是否为浅代理来决定是直接返回,还是返回嵌套对象的只读|普通响应式代理
const get = /*#__PURE__*/ createGetter()
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// ReactiveFlags 是在reactive中声明的枚举值,如果key是枚举值则直接返回对应的布尔值
if (key === ReactiveFlags.IS_REACTIVE) return !isReadonly
else if (key === ReactiveFlags.IS_READONLY) return isReadonly
else if (key === ReactiveFlags.RAW) return target
const targetIsArray = isArray(target)
// 如果目标对象是数组并且 key 属于三个方法之一 ['includes', 'indexOf', 'lastIndexOf'],即触发了这三个操作之一
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
// 如果 key 是 symbol 内置方法,或者访问的是原型对象,直接返回结果,不收集依赖;
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 如果非只读,则通过 track 函数设置副作用跟踪;
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 如果是浅层代理模式则直接返回 GET 操作的结果;
if (shallow) {
return res
}
if (isRef(res)) {
// 如果get的结果是ref,通过 shouldUnwrap 判断是否需要解包;
// 这里也明确规定了只有对象的某些属性会被解包,数组以及对index属性则不会解包;
// ref unwrapping - does not apply for Array + integer key.
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : res
}
// 由于 proxy 初始 set 的时候可能只代理了一层,所以 target[key] 的值如果是对象,就继续对其设置代理
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
Has Handler
has
代理比较简单,单纯的判断一下是否为内置属性|Symbol
属性,然后触发 TrackOpTypes.HAS
类型的 track
操作记录副作用。
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
}
DeleteProperty Handler
删除属性的操作执行成功之后会触发 TriggerOpTypes.DELETE
类型的 trigger
操作:
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
}
OwnKeys Handler
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
otherHandlers
至于其他的 getter|setter
只是做了一些调参:
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
const shallowSet = /*#__PURE__*/ createSetter(true)
readonly
readonlyHandlers
禁止删除和修改:
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
set(target, key) {
if (__DEV__) {
console.warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
},
deleteProperty(target, key) {
if (__DEV__) {
console.warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
}
}
shallowReactiveHandlers
在 getter
和 setter
中阻止嵌套下响应式:
export const shallowReactiveHandlers: ProxyHandler<object> = extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet
}
)
shallowReadonlyHandlers
上面两者的结合
// 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: ProxyHandler<object> = extend(
{},
readonlyHandlers,
{
get: shallowReadonlyGet
}
)