mutableHandlers 执行过程探究
baseHandlers 是在源码的 packages --> reactivity --> src --> baseHandlers .ts
baseHandlers 是当代理对象为 Object(普通的对象) 和 Array 的 handler 即 new Proxy(Target,badeHandlers),baseHandlers 处理器传入的值为 mutableHandlers 对象包含了 get,set,deleteProperty,has,ownKeys 5个方法,对了 读,写,删除,in ,for in 这5个操作。
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
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
)
}
下面我们分别看下这 5 个方法
get/has/ownKeys
get 函数是执行 createGetter 方法后得到的, createGetter 接收两个参数默认为 false,分别控制是否为浅响应(shallowGet:只对对象最外层做响应式处理),只读(readonlyGet:数据只能读不能写),浅只读(shallowReadonlyGet:只限制最外层不能改,并且只对外层做响应式处理)。get 函数主要做的以下5件事情:
- 对特殊的key值的处理
- 被代理对象为数组的处理
- 深响应,浅响应,只读,浅只读的处理
- 读取值,并且实现自动脱ref
- track 函数执行,进行依赖收集
细节部分如下:
const get = /*#__PURE__*/ createGetter()
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
IS_SHALLOW = '__v_isShallow',
RAW = '__v_raw'
}
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
//特殊key值的处理
if (key === ReactiveFlags.IS_REACTIVE) { // key === '__v_isReactive',如果不是只读,则是响应式
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) { // key === '__v_isReadonly',根据传入的 isReadonly 参数决定
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) { // key === '__v_isShallow',根据传入的 shallow 参数决定
return shallow
} else if ( // key === '__v_raw',并且已经记录在 WeakMap 对象内,返回被代理对象,用于 toRaw 方法
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
// 数组的处理,针对数组查找方法 'includes', 'indexOf', 'lastIndexOf',
// 影响数组长度方法 'push', 'pop', 'shift', 'unshift', 'splice' 做特殊的处理
/**
const arrayInstrumentations = createArrayInstrumentations()
function createArrayInstrumentations() {
(['includes', 'indexOf', 'lastIndexOf'] as const).forEach( ... )
(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach( ... )
}
*/
const targetIsArray = isArray(target)
if (!isReadonly) {
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
// 读取属性值,直接返回结果
const res = Reflect.get(target, key, receiver)
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// track 函数执行,进行依赖收集
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 浅响应直接返回结果
if (shallow) {
return res
}
// 自动脱 ref :读取的值是 ref ,返回它的 value 属性值
if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 读取的属性值是对象,并且是只读,递归调用 readonly,非只读递归调用 reactive
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
}
}
has 和 ownKeys 这两个方法相对简单,仅仅是拦截操作调用 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
}
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
set/deleteProperty
set 函数是执行 createSetter 方法后得到的, createSetter 接收一个参数默认 false,区分是否为浅响应。set 函数主要做的以下3件事情:
- 区分操作是新增还是修改
- 设置值
- 调用 trigger 函数派发更新
const set = /*#__PURE__*/ createSetter()
const shallowSet = /*#__PURE__*/ createSetter(true)
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
let oldValue = (target as any)[key]
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
if (!shallow) {
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(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 =
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
// target 和 被代理对象判断是为了解决 key 值是在原型链上的属性,set 时会触发两次 trigger 的问题
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
deleteProperty 也相对简单,拦截删除操作调用 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
}
mutableCollectionHandlers 执行过程探究
mutableCollectionHandlers 是在源码的 packages --> reactivity --> src --> collectionHandlers .ts
mutableCollectionHandlers 是当代理对象为 Map,Set,WeakMap,WeakSet 的 handler 即 new Proxy(Target,mutableCollectionHandlers ),mutableCollectionHandlers 仅有 get 操作。
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}
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
)
}
Map,Set,WeakMap,WeakSet 为什么只需要拦截 get 操作呢,这是因为这些集合类对象的操作,都是通过调用对象上的方法和属性来完成的。通过拦截 get 操作,key 值传递的是方法名, 重写方法即可。
对于 get 操作的拦截还有一点需要注意:
new Proxy(target,
{
get(target, key, recevier) {
// 不能直接返回 Reflect.get ,否则会一直递归导致栈溢出
// return Reflect.get(target, key, recevier)
return () => {
// 返回的其实需要执行的方法而不是属性
return Reflect.get(target, key, recevier)
}
}
})
接下来看下 createInstrumentationGetter 函数的执行,该函数接收两个参数默认都为 false,用来控制是否为浅响应还是只读。主要完成2件事件:
- 对特殊的key值的处理
- 根据传入的参数选择合适的 instrumentations
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)
}
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
)
}
}
instrumentations 又是调用 createInstrumentations 函数生成的,可以看出对于集合对象的 get,has,add,set,delete,clear,forEach 方法,size 属性均进行了重写
const [
mutableInstrumentations,
readonlyInstrumentations,
shallowInstrumentations,
shallowReadonlyInstrumentations
] = /* #__PURE__*/ createInstrumentations()
function createInstrumentations() {
const mutableInstrumentations: Record<string, Function | number> = {
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)
}
const shallowInstrumentations: Record<string, Function | number> = {
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)
}
const readonlyInstrumentations: Record<string, Function | number> = {
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)
}
const shallowReadonlyInstrumentations: Record<string, Function | number> = {
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)
}
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach( ... )
return [
mutableInstrumentations,
readonlyInstrumentations,
shallowInstrumentations,
shallowReadonlyInstrumentations
]
}
get
get 函数接收 4 个参数,isReadonly,isShallow 默认为 false,主要功能如下:
- track 函数执行,进行依赖收集
- 根据 isReadonly,isShallow 是否需要深响应和只读
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)
// wrap 是根据 isReadonly,isShallow 选择的函数,其值如下:
/**
const toShallow = <T extends unknown>(value: T): T => value
export const toReadonly = <T extends unknown>(value: T): T =>
isObject(value) ? readonly(value as Record<any, any>) : value
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
*/
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)
}
}
set
set 函数作用和主要做的以下2件事情:
- 区分操作是新增还是修改
- 调用 trigger 函数派发更新
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
}
最后总结下 handler,当 target 是 Object 和 Array 时,handler 为 mutableHandlers,会劫持 get,set,has,deleteProperty,ownKeys,对属性进行依赖收集和派发更新;当 target 是 Map,Set,WeakMap,WeakSet时,handler 为 mutableCollectionHandlers,只拦截 get,对方法进行依赖收集和派发更新