baseHandlers.ts
packages/reactivity/src/baseHandlers.ts 是 reactive()/shallowReactive()/readonly()/shallowReadonly() 响应式API的 Proxy 陷阱处理文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export const mutableHandlers : ProxyHandler <object > = new MutableReactiveHandler ()export const readonlyHandlers : ProxyHandler <object > = new ReadonlyReactiveHandler ()export const shallowReactiveHandlers : MutableReactiveHandler = new MutableReactiveHandler (true )export const shallowReadonlyHandlers : ReadonlyReactiveHandler = new ReadonlyReactiveHandler (true )
BaseReactiveHandler 类中封装了 Proxy 中的 handler.get 陷阱。
MutableReactiveHandler 类继承 BaseReactiveHandler 类,组成完整的非 readonly 类型对象陷阱,其中包含
handler.get 陷阱
handler.set 陷阱
handler.deleteProperty 陷阱
handler.has 陷阱
handler.ownkeys 陷阱
ReadonlyReactiveHandler 类继承 BaseReactiveHandler 类,组成完整的 readonly 类型对象陷阱,其中包含
handler.get 陷阱
handler.set 陷阱
handler.deleteProperty 陷阱
BaseReactiveHandler
get 陷阱源码
1 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 class BaseReactiveHandler implements ProxyHandler <Target > { constructor ( protected readonly _isReadonly = false , protected readonly _isShallow = false , ) {} get (target: Target, key: string | symbol, receiver: object ): any { if (key === ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP] const isReadonly = this ._isReadonly, isShallow = this ._isShallow if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { return isReadonly } else if (key === ReactiveFlags.IS_SHALLOW) { return isShallow } else if (key === ReactiveFlags.RAW) { if ( receiver === (isReadonly ? isShallow ? shallowReadonlyMap : readonlyMap : isShallow ? shallowReactiveMap : reactiveMap ).get (target) || Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver) ) { return target } return } const targetIsArray = isArray(target) if (!isReadonly) { let fn: Function | undefined 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 (!isReadonly) { track(target, TrackOpTypes.GET, key) } if (isShallow) { return res } if (isRef(res)) { const value = targetIsArray && isIntegerKey(key) ? res : res.value return isReadonly && isObject(value) ? readonly(value) : value } if (isObject(res)) { return isReadonly ? readonly(res) : reactive(res) } return res } }
get ReactiveFlags
1 2 3 4 5 6 7 8 9 10 11 12 if (key === ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP]const isReadonly = this ._isReadonly, isShallow = this ._isShallowif (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { return isReadonly } else if (key === ReactiveFlags.IS_SHALLOW) { return isShallow }
如果 get 代理对象的 ReactiveFlags.SKIP/ReactiveFlags.IS_REACTIVE/ReactiveFlags.IS_READONLY/ReactiveFlags.IS_SHALLOW 属性,直接返回对应的属性。
reactive()
shallowReactive()
readonly()
shallowReadonly()
ReactiveFlags.SKIP
undefined
undefined
undefined
undefined
ReactiveFlags.IS_REACTIVE
true
true
false
false
ReactiveFlags.IS_READONLY
false
false
true
true
ReactiveFlags.IS_SHALLOW
false
true
false
true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const obj1 = reactive<Record<string , unknown >>({}) const obj2 = shallowReactive<Record<string , unknown >>({}) const obj3 = readonly <Record<string , unknown >>({}) const obj4 = shallowReadonly<Record<string , unknown >>({}) ;[obj1, obj2, obj3, obj4].forEach((item) => { console.log ([ item[ReactiveFlags.SKIP], item[ReactiveFlags.IS_REACTIVE], item[ReactiveFlags.IS_READONLY], item[ReactiveFlags.IS_SHALLOW], ]) }) // [undefined, true , false , false ] // [undefined, true , false , true ] // [undefined, false , true , false ] // [undefined, false , true , true ]
get ReactiveFlags.RAW
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 if (key === ReactiveFlags.RAW) { if ( receiver === (isReadonly ? isShallow ? shallowReadonlyMap : readonlyMap : isShallow ? shallowReactiveMap : reactiveMap ).get (target) || Object.getPrototypeOf (target) === Object.getPrototypeOf (receiver) ) { return target } return }
如果 get 代理对象的 ReactiveFlags.RAW 属性:
首先根据 isReadonly 和 isShallow 的值,从对应的 WeakMap 中获取代理对象。如果 receiver 等于代理对象,则返回对应的原始对象。
1 2 3 4 5 6 7 8 const original = const proxy = reactive<Record <string , unknown>>(original)const proxyRaw = proxy[ReactiveFlags.RAW]
其次比较 Object.getPrototypeOf(userProxy) 和 Object.getPrototypeOf(userProxy)的值。当用户使用 Object.create() 或自定义代理时,receiver 可能不在 Vue 的内部 WeakMap 中,但语义上它仍然是 Vue 代理的“包装”或“扩展”。如果两者相等,也返回对应的原始对象。
备注:假设有一段代码执行 obj.name ", obj 不是一个 proxy,且自身不含 name 属性,但是它的原型链上有一个 proxy,那么,那个 proxy 的 get() 处理器会被调用,而此时,obj 会作为 receiver 参数传进来。
正常get
原型链get
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const original = { foo : 1 }const vueProxy = reactive<Record <string , unknown >>(original)const userProxy = new Proxy (vueProxy, { get (target, key, receiver ) { if (key === 'foo' ) { console .log ('访问 foo' ) } return Reflect .get (target, key, receiver) }, })const raw = userProxy[ReactiveFlags .RAW ]console .log ('raw' , raw)
get arr.fn/hasOwnProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 export const isArray : typeof Array .isArray = Array .isArray function hasOwnProperty (this : object , key : unknown ) { if (!isSymbol (key)) key = String (key) const obj = toRaw (this ) track (obj, TrackOpTypes .HAS , key) return obj.hasOwnProperty (key as string ) }const targetIsArray = isArray (target)if (!isReadonly) { let fn : Function | undefined if (targetIsArray && (fn = arrayInstrumentations[key])) { return fn } if (key === 'hasOwnProperty' ) { return hasOwnProperty } }
处理响应式对象的特定属性访问。
如果 target 是数组,且 key 为数组的方法,返回 arrayInstrumentations 中定义的数组方法(劫持数组方法)。
如果 key 为 hasOwnProperty,触发 track(track(obj, TrackOpTypes.HAS, key)) 依赖收集, 同时将响应式对象转换为原始对象,返回原始对象的 hasOwnProperty 结果。
Reflect.get
1 2 3 4 5 6 7 8 9 const res = Reflect.get( target , key, isRef(target ) ? target : receiver, )
使用 Reflect.get 方法获取 handler.get 方法的操作结果。
get Symbol/nonTrackableKeys
1 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 export function makeMap (str : string ): (key : string ) => boolean { const map = Object .create (null ) for (const key of str.split (',' )) map[key] = 1 return val => val in map }export const isSymbol = (val : unknown ): val is symbol => typeof val === 'symbol' const isNonTrackableKeys = makeMap (`__proto__,__v_isRef,__isVue` )const builtInSymbols = new Set ( Object .getOwnPropertyNames (Symbol ) .filter (key => key !== 'arguments' && key !== 'caller' ) .map (key => Symbol [key as keyof SymbolConstructor ]) .filter (isSymbol), )if (isSymbol (key) ? builtInSymbols.has (key) : isNonTrackableKeys (key)) { return res }const isNonTrackableKeys = makeMap (`__proto__,__v_isRef,__isVue` )
如果 key 为 Symbol 类型,且在 builtInSymbols 中,即 key 为 JavaScript 内置的 Symbol 类型,返回 handler.get 结果.
如果 key 不为 Symbol 类型,且 key 为不可跟踪的键: __proto__/__v_isRef/__isVue ,直接返回操作结果。
track()
1 2 3 4 if (!isReadonly) { track (target, TrackOpTypes.GET, key) }
如果为非 readonly 类型对象触发 handler.get 陷阱,触发 track(target, TrackOpTypes.GET, key) 依赖收集。
get shallow
1 2 3 4 // packages/reactivity /src/base Handlers.tsif (isShallow) { return res }
如果为浅响应式对象触发 handler.get 陷阱,直接返回操作结果。
get 结果为ref
1 2 3 4 5 6 // packages/reactivity/src/baseHandlers.tsif (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key . const value = targetIsArray && isIntegerKey(key ) ? res : res.value return isReadonly && isObject(value) ? readonly(value) : value }
处理 res 为 ref 响应式对象情况。
如果 target 是数组,且 key 为整数键,不对 ref 对象进行解包,否则 ref 对象的解包结果。
非数组ref解包取值
数组ref不解包取值
如果为 readonly 类型,且 value 为对象,返回 readonly 类型的解包结果,否则返回 value。
isReadonly && isObject(value)取值
!(isReadonly && isObject(value))取值
get 结果为object
1 2 3 4 5 6 7 if (isObject(res)) { return isReadonly ? readonly (res) : reactive (res) }
如果 handler.get 结果为对象,且 Handler 为 readonly 类型,返回 readonly 包装结果。
如果 handler.get 结果为对象,且 Handler 不为 readonly 类型,返回 reactive 包装结果。
处理其他情况,直接返回操作结果。
MutableReactiveHandler
MutableReactiveHandler源码
1 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 class MutableReactiveHandler extends BaseReactiveHandler { constructor (isShallow = false ) { super (false , isShallow) } set ( target : Record <string | symbol , unknown >, key : string | symbol , value : unknown , receiver : object , ): boolean { let oldValue = target[key] const isArrayWithIntegerKey = isArray (target) && isIntegerKey (key) if (!this ._isShallow ) { const isOldValueReadonly = isReadonly (oldValue) if (!isShallow (value) && !isReadonly (value)) { oldValue = toRaw (oldValue) value = toRaw (value) } if (!isArrayWithIntegerKey && isRef (oldValue) && !isRef (value)) { if (isOldValueReadonly) { if (__DEV__) { warn ( `Set operation on key "${String (key)} " failed: target is readonly.` , target[key], ) } return true } else { oldValue.value = value return true } } } else { } const hadKey = isArrayWithIntegerKey ? Number (key) < target.length : hasOwn (target, key) const result = Reflect .set ( target, key, value, isRef (target) ? target : receiver, ) if (target === toRaw (receiver) && result) { if (!hadKey) { trigger (target, TriggerOpTypes .ADD , key, value) } else if (hasChanged (value, oldValue)) { trigger (target, TriggerOpTypes .SET , key, value, oldValue) } } return result } deleteProperty ( target : Record <string | symbol , unknown >, key : string | symbol , ): boolean { const hadKey = hasOwn (target, key) const oldValue = target[key] const result = Reflect .deleteProperty (target, key) if (result && hadKey) { trigger (target, TriggerOpTypes .DELETE , key, undefined , oldValue) } return result } has (target : Record <string | symbol , unknown >, key : string | symbol ): boolean { const result = Reflect .has (target, key) if (!isSymbol (key) || !builtInSymbols.has (key)) { track (target, TrackOpTypes .HAS , key) } return result } ownKeys (target : Record <string | symbol , unknown >): (string | symbol )[] { track ( target, TrackOpTypes .ITERATE , isArray (target) ? 'length' : ITERATE_KEY , ) return Reflect .ownKeys (target) } }
定义了响应式对象的代理拦截函数,包括 set、deleteProperty、has、ownKeys 四个拦截器。
set 陷阱
handler.set() 会拦截目标对象的以下操作:
proxy[key] = value 和 proxy.key = value
Object.create(proxy)[key] = value
Reflect.set()
set陷阱源码
1 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 set ( target: Record<string | key: string | value: unknown, receiver: object, ): boolean let const if const if oldValue value } if if if warn( `Set ` target[key], ) } return } else oldValue.value return } } } else } const ? Number(key) : hasOwn(target, key) const target, key, value, isRef(target) ) if if trigger(target, TriggerOpTypes.ADD, key, value) } else trigger(target, TriggerOpTypes.SET , key, value, oldValue) } } return }
shallow 判断
1 2 3 4 if (!this ._isShallow) { ... }else {}
判断是否为浅层响应式对象,如果不是浅层响应式对象,执行额外操作。
reactive响应式赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export enum ReactiveFlags { IS_SHALLOW = '__v_isShallow' , }export function isShallow (value : unknown ): boolean { return !!(value && (value as Target )[ReactiveFlags .IS_SHALLOW ]) }export function isReadonly (value : unknown ): boolean { return !!(value && (value as Target )[ReactiveFlags .IS_READONLY ]) }export function toRaw<T>(observed : T): T { const raw = observed && (observed as Target )[ReactiveFlags .RAW ] return raw ? toRaw (raw) : observed }if (!isShallow (value) && !isReadonly (value)) { oldValue = toRaw (oldValue) value = toRaw (value) }
如果 value 不是浅层响应式,也不是只读响应式,设置 oldValue 和 value 的值。
因为值可能是响应式对象,所以需要使用 toRaw 函数获取其原始对象。
对象旧值ref解包
1 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 export enum ReactiveFlags { IS_READONLY = '__v_isReadonly' , }export function isReadonly (value : unknown ): boolean { return !!(value && (value as Target )[ReactiveFlags .IS_READONLY ]) }export const isArray : typeof Array .isArray = Array .isArray export const isString = (val : unknown ): val is string => typeof val === 'string' export const isIntegerKey = (key : unknown ): boolean => isString (key) && key !== 'NaN' && key[0 ] !== '-' && '' + parseInt (key, 10 ) === keyconst isArrayWithIntegerKey = isArray (target) && isIntegerKey (key)const isOldValueReadonly = isReadonly (oldValue)if (!isArrayWithIntegerKey && isRef (oldValue) && !isRef (value)) { if (isOldValueReadonly) { if (__DEV__) { warn ( `Set operation on key "${String (key)} " failed: target is readonly.` , target[key], ) } return true } else { oldValue.value = value return true } }
isArray(target) && isIntegerKey(key) 用于判断 target 是否为数组且调用 set 操作的 key 是否为整数键。并将判断结果赋值给 isArrayWithIntegerKey。
!isArrayWithIntegerKey && isRef(oldValue) && !isRef(value) 如果操作的不是数组键,且旧值 oldValue 是 ref 对象,且新值 value 不是 ref 对象时。
如果 oldValue 是只读响应式,测试环境会触发 warn 函数,提示用户不能对只读响应式属性进行 set 操作并中断后续操作。
如果 oldValue 不是只读响应式,设置 oldValue.value 的值为 value。
该逻辑为 reactive 对象的 ref 自动解包逻辑。
Reflect.set()
1 2 3 4 5 6 const result = Reflect.set( target , key, value, isRef(target ) ? target : receiver, )
Reflect.set 执行实际赋值,同时将操作结果赋值给 result 。
trigger(ADD/SET)
1 2 3 4 5 6 7 8 9 10 11 const hadKey = isArrayWithIntegerKey ? Number(key ) < target.length : hasOwn (target, key )if (target === toRaw(receiver) && result) { if (!hadKey) { trigger(target, TriggerOpTypes.ADD , key , value) } else if (hasChanged(value, oldValue)) { trigger(target, TriggerOpTypes.SET , key , value, oldValue) } }
hadKey 用于判断 set 的 key 属性是否为 target 中已存在的 key。如果 hadKey 为 true,说明本次 set 操作类型为 TriggerOpTypes.SET,否则为 TriggerOpTypes.ADD。
target === toRaw(receiver) 确保修改的是当前对象自身的属性,而非原型链上的属性,且 set 操作成功。
如果 set 的是新增属性,触发 trigger(TriggerOpTypes.ADD) 。
如果 set 的是已存在的属性,且值有变化,触发 trigger(TriggerOpTypes.SET)。
deleteProperty 陷阱
handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作。
delete proxy[key] 和 delete proxy.key
Reflect.deleteProperty()
deleteProperty 陷阱源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const hasOwnProperty = Object .prototype .hasOwnProperty export const hasOwn = ( val : object , key : string | symbol , ): key is keyof typeof val => hasOwnProperty.call (val, key)deleteProperty ( target : Record <string | symbol , unknown >, key : string | symbol , ): boolean { const hadKey = hasOwn (target, key) const oldValue = target[key] const result = Reflect .deleteProperty (target, key) if (result && hadKey) { trigger (target, TriggerOpTypes .DELETE , key, undefined , oldValue) } return result }
hadKey: 通过 Object.prototype.hasOwnProperty 方法判断属性是否存在为对象的直接属性,而不是从原型链继承的属性。
oldValue: 删除属性前的属性值。
result: 调用原生的 Reflect.deleteProperty 执行实际的删除操作,返回 true/false 表示删除是否成功。
当操作成功且删除的是自有属性时,触发 trigger(TriggerOpTypes.DELETE),通知依赖收集器更新依赖。
触发示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const obj = reactive<Record <string , unknown >>({ name : 'Vue' , age : 18 , })watchEffect (() => { const result = 'age' in obj console .log (result) })delete obj.age
has 陷阱
handler.has 钩子可以拦截下面这些操作:
key in proxy
key in Object.create(proxy)
with(proxy) { (key); }
Reflect.has()
has 陷阱源码
1 2 3 4 5 6 7 8 // packages/reactivity/src/baseHandlers.ts has(target: Record<string | symbol, unknown >, key : string | symbol): boolean { const result = Reflect.has(target, key ) if (!isSymbol(key ) || !builtInSymbols.has(key )) { track (target, TrackOpTypes.HAS, key ) } return result }
调用原生的 Reflect.has 执行实际的 in 操作,获取真实的检查结果。判断 key 是否为内置 Symbol 类型,如果不是,则触发 track 方法完成依赖收集。最后返回真实的检查结果。
has 拦截器填补了一个重要的空白:in 操作符本身不依赖属性值,只依赖属性是否存在。如果没有 has 拦截器,删除属性后 in 操作的结果变化无法被响应式系统捕获。
触发示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const obj = reactive<Record <string , unknown >>({ name : 'Vue' , age : 18 , })watchEffect (() => { const result = 'age' in obj console .log (result) })delete obj.age
ownKeys 陷阱
handler.ownKeys() 可以拦截以下操作:
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()
for...in 循环
ownKeys 陷阱源码
1 2 3 4 5 6 7 8 9 ownKeys (target : Record <string | symbol , unknown >): (string | symbol )[] { track ( target, TrackOpTypes .ITERATE , isArray (target) ? 'length' : ITERATE_KEY , ) return Reflect .ownKeys (target) }
当触发 handler.ownKeys() 时,触发 track 方法完成依赖收集,在完成依赖收集后,调用原生的 Reflect.ownKeys 返回真实的属性列表。
触发示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const obj = reactive<Record <string, unknown >>({ name : 'Vue' , age: 18 , }) watchEffect(() => { // Object .keys 触发 handler .ownKeys 依赖 const keys = Object .keys(obj) console.log(keys) }) // 新增属性,触发 handler .set 中的依赖更新(trigger ) obj.hobby = 'reading' nextTick(() => { // 删除属性,触发 handler .deleteProperty 中的依赖更新(trigger ) delete obj.name }) // ['name' , 'age' ] // ['name' , 'age' , 'hobby' ] // ['age' , 'hobby' ]
ReadonlyReactiveHandler
ReadonlyReactiveHandler 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ReadonlyReactiveHandler extends BaseReactiveHandler { constructor (isShallow = false ) { super (true , isShallow) } set (target : object , key : string | symbol ) { if (__DEV__) { warn ( `Set operation on key "${String (key)} " failed: target is readonly.` , target, ) } return true } deleteProperty (target : object , key : string | symbol ) { if (__DEV__) { warn ( `Delete operation on key "${String (key)} " failed: target is readonly.` , target, ) } return true } }
set 陷阱
set 陷阱源码
1 2 3 4 5 6 7 8 9 10 set (target : object , key : string | symbol ) { if (__DEV__) { warn ( `Set operation on key "${String (key)} " failed: target is readonly.` , target, ) } return true }
对只读响应式对象触发 handler.set 陷阱时,测试环境触发警告,生产环境静默失败处理。
deleteProperty 陷阱
deleteProperty 陷阱源码
1 2 3 4 5 6 7 8 9 deleteProperty (target : object , key : string | symbol ) { if (__DEV__) { warn ( `Delete operation on key "${String (key)} " failed: target is readonly.` , target, ) } return true }
对只读响应式对象触发 handler.deleteProperty 陷阱时,测试环境触发警告,生产环境静默失败处理。