与ref用法类似,
不同点:
- ref用于基本数据类型
- reactive 用于复杂数据类型
export function reactive(target: object) {
// 如果是readonly对象的代理,那么这个对象是不可观察的,直接返回readonly对象的代理
if (readonlyToRaw.has(target)) {
return target
}
// 如果是readonly原始对象,那么这个对象也是不可观察的,直接返回readonly对象的代理,这里使用readonly调用,可以拿到readonly对象的代理
if (readonlyValues.has(target)) {
return readonly(target)
}
// 调用createReactiveObject创建reactive对象
return createReactiveObject(
target, // 目标对象
rawToReactive, // 原始对象映射响应式对象的WeakMap
reactiveToRaw, // 响应式对象映射原始对象的WeakMap
mutableHandlers, // 响应式数据的代理handler,一般是Object和Array
mutableCollectionHandlers // 响应式集合的代理handler,一般是Set、Map、WeakMap、WeakSet
)
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
if (!isObject(target)) {
return target
}
// 如果已经是proxy对象则直接返回,有个例外,如果是readOnly作用于响应式
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
const proxyMap = isReadonly ? readonlyMap : reactiveMap
// 已经有了对应的proxy映射 直接
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
const targetType = getTargetType(target)
// 只有在白名单中的数据类型才可以被响应式
if (targetType === TargetType.INVALID) {
return target
}
// 通过Proxy API劫持target对象,把它变成响应式
const proxy = new Proxy(
target,
// Map Set WeakMap WeakSet用collectionhandlers代理 Object Array用baseHandlers代理
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
// 存储一个原始类型和proxy数据类型的映射
proxyMap.set(target, proxy)
return proxy
}
可相应式数据类型白名单
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON // 1
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION // 2
default:
return TargetType.INVALID // 0
}
}
总结:
reactive创建对象的过程
\
-
如果传入的是只读类型的响应式对象 直接返回该响应式对象
-
否则执行createReactiveObject 方法
a. 如果不是对象则返回(并报警告)
b. 如果已经是proxy对象则直接返回,有个例外,如果是readOnly作用于响应式
c. 判断是否存在代理映射,从相应的map集合(readonlyMap、reactiveMap)中取值,并返回,若无,继续向下执行
d. 判断数据类型是否是白名单类型,若不是,直接返回(0->代表为非白名单类型,1->基本数据类型(包括数组和对象),2->集合类型)
e. 若是,可通过Proxy API劫持target对象,把它变成响应式,并将其存储至proxyMap集合中
集合类型处理器mutableHandlers(collectionHandlers)
这个方法实际上就是对目标对象的一些访问、删除、查询、设置的操作的劫持
const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()
export const mutableHandlers: ProxyHandler<object> = {
get, // 对数据的读取属性进行拦截 包含target.语法和target[]
set, // 对数据的存入属性进行拦截
deleteProperty, // delete操作符进行拦截 可以监听到属性的删除操作
has, // 对对象的in操作符进行属性拦截
ownKeys // 访问对象属性名的时候会触发ownKeys函数
}
在这主要看,get和set
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// 求值
const res = Reflect.get(target, key, receiver)
if (!isReadonly) {
// 依赖收集
track(target, TrackOpTypes.GET, key)
}
// 递归调用响应式
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
// 返回结果
return res
}
}
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
// 1.先获取oldValue
const oldValue = (target as any)[key]
// 2.设置新值
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(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
首先会使用Reflect.get进行求值, 然后判断是否是只读的,
如果不是就调用track
进行依赖收集,然后对求值的结果进行判断,
如果是对象则递归调用reactive
或者readonly
对结果继续进行响应式处理,最后将获取的结果返回。
注意:
- vue2.0 初始化就对整个对象进行递归相应
- vue3.0 初始化时只对第一层属性进行相应式,当返回proxy的属性被访且是对象的话再进行递归响应式,Proxy劫持的是对象本身,并不能劫持子对象的变化,正是利用这种特性可以延时定义子对象响应式的实现,在初始化的时候性能也会得到提升。