reactive()
reactive() 返回一个对象的响应式代理。是 vue 响应式 api 之一。
1 function reactive< T extends object> ( target: T ) : UnwrapNestedRefs< T >
响应式转换是“深层”的:它会影响到所有嵌套的属性。
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。
一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包 。
1 2 3 4 5 6 7 8 9 10 11 12 13 const age = ref(18)const ageList = reactive([age])const personal = reactive({ age, })const age1 = ageList[0]personal .age++const age2 = personal .age console.log ('age1', age1) console.log ('age2', age2)
响应式示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { reactive } from 'vue' const obj = reactive ({ nested: { count: 0 }, arr: ['foo' , 'bar' ] , })watchEffect (() => { console.log (obj.nested .count) })watchEffect (() => { console.log (obj.arr .length) }) obj.nested .count = 1000 obj.arr .push ('baz' )
reactive 源码实现
reactive()
reactive() 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 export function reactive (target : object) { if (isReadonly(target )) { return target } return createReactiveObject( target , false , mutableHandlers, mutableCollectionHandlers, reactiveMap, ) }
通过 isReadonly 函数判断是直接返回 target 还是通过 createReactiveObject 函数创建一个响应式代理。
isReadonly()
1 2 3 if (isReadonly(target )) { return target }
判断 target 是否为只读对象,即 target 的 ReactiveFlags.IS_READONLY(__v_isReadonly) 属性值为 true,ReactiveFlags 取值相关逻辑位于 /packages/reactivity/src/baseHandlers.ts 的 BaseReactiveHandler.get 方法中。
应用示例
1 2 3 4 5 6 7 8 9 const ageList = readonly ({ age: 18 , })const ageList2 = reactive(ageList)const ageList3 = shallowReadonly(ageList) console.log (ageList === ageList2) console.log (ageList2 === ageList3) console.log (ageList === ageList3)
具有 ReactiveFlags.IS_READONLY 属性的只读对象被其它响应式api包裹时,会直接返回原对象。
1 2 3 4 5 6 7 8 // packages/reactivity/src/reactive.ts createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap, )
通过 createReactiveObject 函数创建一个响应式对象。
target: 目标对象,即要创建响应式代理的对象。
isReadonly: 创建的代理对象是否为 readonly 类型,默认值为 false。
mutableHandlers: Object/Array 的 ProxyHandler 对象。
mutableCollectionHandlers: Map/Set/WeakMap/WeakSet的 ProxyHandler 对象。
reactiveMap: 已创建响应式代理的WeakMap,键为target,值为响应式代理。
createReactiveObject()
createReactiveObject源码
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 function createReactiveObject ( target : Target , isReadonly : boolean , baseHandlers : ProxyHandler <any >, collectionHandlers : ProxyHandler <any >, proxyMap : WeakMap <Target , any >, ) { if (!isObject (target)) { if (__DEV__) { warn ( `value cannot be made ${isReadonly ? 'readonly' : 'reactive' } : ${String ( target, )} ` , ) } return target } if ( target[ReactiveFlags .RAW ] && !(isReadonly && target[ReactiveFlags .IS_REACTIVE ]) ) { return target } if (target[ReactiveFlags .SKIP ] || !Object .isExtensible (target)) { return target } const existingProxy = proxyMap.get (target) if (existingProxy) { return existingProxy } const targetType = targetTypeMap (toRawType (target)) if (targetType === TargetType .INVALID ) { return target } const proxy = new Proxy ( target, targetType === TargetType .COLLECTION ? collectionHandlers : baseHandlers, ) proxyMap.set (target, proxy) return proxy }
非 Object 处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export const isObject = (val : unknown ): val is Record <any , any > => val !== null && typeof val === 'object' if (!isObject (target)) { if (__DEV__) { warn ( `value cannot be made ${isReadonly ? 'readonly' : 'reactive' } : ${String ( target, )} ` , ) } return target }
通过 isObject() 函数判断 target 是否为对象,因为 new Proxy 只能代理对象,所以如果不是对象,则直接返回 target。如果是 dev 环境,通过 warn() 函数输出警告信息。
readonly 类型处理
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 class BaseReactiveHandler implements ProxyHandler <Target > { get (target : Target , key : string | symbol , receiver : object ): any { if (key === ReactiveFlags .RAW ) { if ( receiver === (isReadonly ? isShallow ? shallowReadonlyMap : readonlyMap : isShallow ? shallowReactiveMap : reactiveMap ).get (target) || Object .getPrototypeOf (target) === Object .getPrototypeOf (receiver) ) { return target } return } } }export enum ReactiveFlags { IS_REACTIVE = '__v_isReactive' , RAW = '__v_raw' , }if ( target[ReactiveFlags .RAW ] && !(isReadonly && target[ReactiveFlags .IS_REACTIVE ]) ) { return target }
target[ReactiveFlags.RAW] 判断对象是否已经是代理对象,如果是代理对象,且重复创建,则直接返回原 target。
情况一
如果当前要创建的不是 readonly 类型代理,!(isReadonly && target[ReactiveFlags.IS_REACTIVE]) 中的 isReadonly 恒为 false,所以 !(false && ...) 恒为 true。即对一个已经是代理对象,且不是创建 readonly 类型的代理对象,返回自身。
1 2 3 4 5 6 7 8 9 10 const original = { foo: 1 }const proxy = reactive(original)const proxy2 = reactive(proxy)const proxy3 = reactive(proxy) // 命中条件:proxy[RAW] 存在 且 isReadonly 为 false , !(fasle && ...) 恒为 true // 执行 return target,即直接返回 proxy,而不是再次包装 console.log (proxy === proxy2) // true console.log (proxy2 === proxy3) // true console.log (proxy === proxy3) // true
情况二
如果当前要创建的是 readonly 类型代理对象,此时判断 target[ReactiveFlags.IS_REACTIVE] 的值,
1 2 3 4 5 6 7 8 9 class BaseReactiveHandler implements ProxyHandler <Target > { get (target: Target, key: string | symbol, receiver: object): any { const isReadonly = this ._isReadonly if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly } } }
方法
isReadonly
ReactiveFlags.IS_REACTIVE
reactive
false
true
shallowReactive
false
true
readonly
true
false
shallowReadonly
true
false
由以上逻辑可知 ReactiveFlags.IS_REACTIVE === !isReadonly。readonly 类型代理对象的 ReactiveFlags.IS_REACTIVE 为 false,非 readonly 类型代理对象的 ReactiveFlags.IS_REACTIVE 为 true。
即如果要创建的是 readonly 类型代理对象,且 target 已经是 readonly 类型代理对象,则直接返回 target。
1 2 3 4 5 6 7 8 9 10 const original = { foo: 1 }const proxy = readonly (original)const proxy2 = shallowReadonly(proxy) console.log (proxy === proxy2)
跳过标记/不可扩展对象处理
1 2 3 4 5 6 7 8 9 export enum ReactiveFlags { SKIP = '__v_skip' , }if (target [ReactiveFlags.SKIP] || !Object.isExtensible(target )) { return target }
ReactiveFlags.SKIP(__v_skip) 是 Vue 内部使用的一个特殊属性,它的核心作用是作为一个“跳过标记”,告诉 Vue 的响应式系统不要将这个对象转换为 Proxy 代理,从而让它保持原始状态,不被响应式化。
Object.isExtensible(obj) 是 JavaScript 用于判断对象是否允许添加新属性的静态方法。
如果 target 已被标记为 “跳过标记” 或者 不允许添加新属性,直接返回 target。
1 2 3 4 5 6 7 8 9 10 11 12 const original = { foo: 1 } // 通过 markRaw 创建标记为 ReactiveFlags.SKIP 的代理对象 const original2 = markRaw({ foo: 1 }) // 通过 Object .freeze (original) 创建一个 Object .isExtensible 为 true 的对象 const obj = reactive(Object .freeze (original)) const obj2 = reactive(original2) // 命中条件:!Object .isExtensible(obj) = true ,未执行 reactive() console.log(isReactive(obj)) // false // 命中条件:ReactiveFlags.SKIP = true ,未执行 reactive() console.log(isReactive(obj2)) // false
target 重复创建处理
1 2 3 4 const existingProxy = proxyMap.get (target)if (existingProxy) { return existingProxy }
proxyMap(一个WeakMap)会缓存已经生成的 Proxy 代理对象。判断 target 是否已经存在对应的 Proxy 代理。
如果 target 已经有对应的 Proxy 代理,直接返回该代理。
1 2 3 4 5 6 const raw = { name : 'Vue' }const proxy1 = reactive (raw) const proxy2 = reactive (raw) console .log (proxy1 === proxy2)
target 合法性校验
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 export const objectToString : typeof Object .prototype .toString = Object .prototype .toString export const toTypeString = (value : unknown ): string => objectToString.call (value)export const toRawType = (value : unknown ): string => { return toTypeString (value).slice (8 , -1 ) }function targetTypeMap (rawType : string ) { switch (rawType) { case 'Object' : case 'Array' : return TargetType .COMMON case 'Map' : case 'Set' : case 'WeakMap' : case 'WeakSet' : return TargetType .COLLECTION default : return TargetType .INVALID } }const targetType = targetTypeMap (toRawType (target))if (targetType === TargetType .INVALID ) { return target }
JavaScript 的 Proxy 只能代理对象(Object),无法代理基础类型。通过 Object.prototype.toString 方法获取 target 的数据类型,如果 target 不是 Object/Array/Map/Set/WeakMap/WeakSet 中的一种,直接返回 target。
1 2 3 4 5 6 const proxy1 = reactive (new Date ())const proxy2 = reactive (document .body )console .log (isReactive (proxy1)) console .log (isReactive (proxy2))
创建 Proxy 代理对象
1 2 3 4 5 6 7 const proxy = new Proxy( target , targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers, ) proxyMap.set (target , proxy)return proxy
Proxy 对象允许你为另一个对象创建代理,该代理能够拦截并重新定义该对象的基本操作。
Proxy 需提供两个参数:
target: 需要代理的原始对象
handle: 定义哪些操作将被拦截以及如何重定义拦截操作的对象
根据 target 的数据类型选择不同的处理函数。如果 target 是 Map/Set/WeakMap/WeakSet 类型,使用 mutableCollectionHandlers 处理器对象,如果 target 是 Object/Array 类型,使用 mutableHandlers 处理器对象。
同时将 target 和 它相应的 proxy 对象存入 proxyMap 中。
最后返回 proxy 对象。
shallowReactive() 源码
1 2 3 4 5 6 7 8 9 10 11 12 export function shallowReactive<T extends object >( target : T, ): ShallowReactive <T> { return createReactiveObject ( target, false , shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap, ) }
readonly() 源码
1 2 3 4 5 6 7 8 9 10 11 export function readonly <T extends object>( target: T, ): DeepReadonly <UnwrapNestedRefs<T>> { return createReactiveObject( target, true , readonly Handlers, readonly CollectionHandlers, readonly Map, ) }
shallowReadonly() 源码
1 2 3 4 5 6 7 8 9 export function shallowReadonly <T extends object>(target: T): Readonly <T> { return createReactiveObject( target, true , shallowReadonly Handlers, shallowReadonly CollectionHandlers, shallowReadonly Map, ) }
注意事项
不能替换整个对象
由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
1 2 3 4 5 6 7 let state = reactive({ count: 0 }) // 上面的 ({ count: 0 }) 引用将不再被追踪 // (响应性连接已丢失!)state = reactive({ count: 1 })
对解构操作不友好
当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 const state = reactive({ count: 0 }) // 当解构时,count 已经与 state .count 断开连接 let { count } = state // 不会影响原始的 state count++ // 该函数接收到的是一个普通的数字 // 并且无法追踪 state .count 的变化 // 我们必须传入整个对象以保持响应性 callSomeFunction(state .count)
原始对象不相等
reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的:
1 2 3 4 5 6 7 const raw = {} const proxy = reactive(raw) // 代理对象和原始对象不是全等的 console.log(proxy = = = raw) // false
isReactive() 源码
1 2 3 4 5 6 export function isReactive (value: unknown): boolean { if (isReadonly(value)) { return isReactive ((value as Target)[ReactiveFlags.RAW] ) } return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE] ) }
检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。
如果 value 是只读对象,递归检查其原始对象。
如果 value 对象的 ReactiveFlags.IS_REACTIVE 属性值为 true,则 value 是由 reactive() 或 shallowReactive() 创建的代理。
否则不是。
使用示例
1 2 3 4 5 6 7 8 9 import { reactive, readonly , isReactive } from 'vue' const proxy = reactive ({ age : 18 , })const proxy2 = readonly (proxy)console .log (isReactive (proxy)) console .log (isReactive (proxy2))
isReadonly() 源码
1 2 3 4 5 6 7 8 9 10 11 export enum ReactiveFlags { ... IS_READONLY = '__v_isReadonly' , ... }export function isReadonly (value : unknown ): boolean { return !!(value && (value as Target )[ReactiveFlags .IS_READONLY ]) }
检查传入的值是否为只读对象,通过 readonly() 和 shallowReadonly() 创建的代理都是只读的。
获取 value 的 ReactiveFlags.IS_READONLY 属性值
如果 value 对象的 ReactiveFlags.IS_READONLY 属性值为 true,则 value 是只读对象。
否则不是。
使用示例
1 2 3 4 5 6 7 const proxy = reactive ({ age: 18 , }) const proxy2 = readonly (proxy) console.log (isReadonly (proxy)) console.log (isReadonly (proxy2))
isShallow() 源码
1 2 3 export function isShallow (value : unknown ): boolean { return !!(value && (value as Target )[ReactiveFlags .IS_SHALLOW ]) }
检查一个对象是否是由 shallowRef()、shallowReactive() 或 shallowReadonly() 创建的代理。
获取 value 的 ReactiveFlags.IS_SHALLOW 属性值
如果属性值为 true,则 value 是 shallow 类型代理对象。
否则不是。
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const proxy = ref ({ age : 18 })const proxy2 = shallowRef ({ age : 18 })const proxy3 = reactive ({ age : 18 })const proxy4 = shallowReactive ({ age : 18 })const proxy5 = readonly ({ age: 18 })const proxy6 = shallowReadonly ({ age : 18 }) ;[proxy, proxy2, proxy3, proxy4, proxy5, proxy6].forEach ((item) => { console.log (isShallow (item)) })
isProxy() 源码
1 2 3 export function isProxy (value: any): boolean { return value ? !!value[ReactiveFlags.RAW] : false }
检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。
获取 value 的 ReactiveFlags.RAW 属性值
如果属性值为 true,则 value 是由 reactive()/readonly()/shallowReactive()/shallowReadonly() 创建的代理。
否则不是。
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const proxy = ref ({ age : 18 })const proxy2 = shallowRef ({ age : 18 })const proxy3 = reactive ({ age : 18 })const proxy4 = shallowReactive ({ age : 18 })const proxy5 = readonly ({ age: 18 })const proxy6 = shallowReadonly ({ age : 18 }) ;[proxy, proxy2, proxy3, proxy4, proxy5, proxy6].forEach ((item) => { console.log (isProxy (item)) })
toRaw() 源码
1 2 3 4 export function toRaw<T> (observed: T): T { const raw = observed && (observed as Target)[ReactiveFlags.RAW] return raw ? toRaw(raw) : observed }
toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。
通过递归调用 toRaw() 函数,获取 observed 对象的 ReactiveFlags.RAW 属性值。
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 const proxy = reactive ({ age : 18 })const proxy2 = shallowReactive ({ age : 18 })const proxy3 = readonly ({ age: 18 })const proxy4 = shallowReadonly ({ age : 18 }) ;[proxy, proxy2, proxy3, proxy4].forEach ((item) => { console.log (toRaw (item)) })
markRaw() 源码
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 const hasOwnProperty = Object .prototype .hasOwnProperty export const hasOwn = ( val : object , key : string | symbol , ): key is keyof typeof val => hasOwnProperty.call (val, key)export const def = ( obj : object , key : string | symbol , value : any , writable = false , ): void => { Object .defineProperty (obj, key, { configurable : true , enumerable : false , writable, value, }) }export function markRaw<T extends object >(value : T): Raw <T> { if (!hasOwn (value, ReactiveFlags .SKIP ) && Object .isExtensible (value)) { def (value, ReactiveFlags .SKIP , true ) } return value }
将一个对象标记为不可被转为代理。返回该对象本身。
通过 hasOwn 函数判断 value 自身是否有 ReactiveFlags.SKIP 属性
如果自身没有 ReactiveFlags.SKIP 属性,且对象可扩展,则调用 def() 定义 value 对象的 ReactiveFlags.SKIP 属性为 true。
否则返回原值 value
使用示例
1 2 3 4 const proxy = markRaw ({ age : 18 })const proxy2 = reactive (proxy )const descriptor = Object .getOwnPropertyDescriptors (proxy )
toReactive() 源码
1 2 3 4 5 6 export const isObject = (val : unknown ): val is Record <any , any > => val !== null && typeof val === 'object' export const toReactive = <T extends unknown >(value : T): T => isObject (value) ? reactive (value) : value
toReactive() 传入一个对象,返回一个对象的响应式代理。
通过 isObject 函数判断 value 是否为对象类型
如果是对象类型,则调用 reactive() 返回对象的响应式代理。
否则返回原值 value
使用示例
1 2 3 4 5 import { toReadonly } from '@vue/reactivity' const proxy = toReadonly ({ age : 18 , })
toReadonly() 源码
1 2 3 4 5 6 7 export const isObject = (val : unknown ): val is Record <any , any > => val !== null && typeof val === 'object' export const toReadonly = <T extends unknown >(value : T): DeepReadonly <T> => isObject (value) ? readonly (value) : (value as DeepReadonly <T>)
检查传入的值是否为只读对象。只读对象的属性可以更改,但它们不能通过传入的对象直接赋值。
isObject 函数判断 value 是否为对象类型
如果是对象类型,则调用 readonly() 返回 readonly 类型代理对象。
否则返回原值 value
使用示例
1 2 3 4 5 import { toReadonly } from '@vue/reactivity' const proxy = toReadonly ({ age : 18 , })