前言
用了组合式api一段时间了,报着知其然、知其所以然的态度去阅读尤大大的代码,分享一下关于reactive部分,按照官网APIreactive部分逐个分析,为了方便理解可以参考简化后代码
Proxy
Proxy相信大家都不陌生了,vue3使用Proxy替代了defineProperty,解决了defineProperty带来的弊端,而reactive部分的实现和Proxy息息相关,不了解Proxy请看阮一峰es6入门,接下来开始源码分析
reactive
看一下源码中是如何定义reactive的
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
// createReactiveObject
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
createReactiveObject方法即为我们创造一个reactive的方法,此部分代码和createReactiveObject简化一下如下:
function reactive(target) {
return createReactiveObject(target, reactiveMap, mutableHandlers)
}
function createReactiveObject(target, proxyMap, baseHandlers) {
// 非对象return
if (!isObject(target)) {
return target
}
// 是否已存在
const existingProxy = proxyMap.get(target)
// 存在返回
if(existingProxy) return existingProxy
// 判断类型
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) return target
// 通过proxy生成新对象
const proxy = new Proxy(target, baseHandlers)
// 缓存数据
proxyMap.set(target, proxy)
return proxy
}
可以看到最终返回的是一个Proxy实例,在简化一下,reactive的实现如下:
function reactive(target) {
return new Proxy(target, {
get,
set
})
}
get、set等的实现至关重要,源码中的实现还是比较复杂的,这里简化一下(尽量使用源码中的代码):
/**
* @param isReadOnly 是否只读
* @param shallow 是否为shallowReadonly、shallowReactive
*/
function createGetter(isReadOnly = false, shallow = false) {
return function get(target, key, receiver) {
// 判断是否为reactive
if(key === IS_REACTIVE) return !isReadOnly
// 判断是否为readonly
else if(key === IS_READONLY) return isReadOnly
// toRaw操作判断
else if(
key === RAW &&
receiver ===
(
isReadOnly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) return target
const result = Reflect.get(target, key, receiver)
// shallow是否为true,若为true不在进行深度处理
if (shallow) return result
// 若为对象,深度处理
if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
return result
}
}
function createSetter() {
return function set(target, key, value, receiver) {
console.log(`${key}发生了改变`)
const result = Reflect.set(target, key, value, receiver)
return result
}
}
const get = createGetter();
const set = createSetter();
此处可以看到rective对对象做了深度处理,简化一下get如下:
function createGetter(isReadOnly = false, shallow = false) {
return function get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
// 若为对象,深度处理
if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
return result
}
}
readonly
当调用readonly方法时,get中isReadOnly将设置为true,并且set、deleteProperty等方法都将重写,阻止并提示只读
const readonlyHandlers = {
get: createGetter(true),
set: (target, key) => {
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
}
}
function readonly(target) {
return createReactiveObject(target, readonlyMap, readonlyHandlers)
}
shallowReactive
当调用shallowReactive方法时,get中shallow将设置为true,数据将不会进行深层次的处理
function shallowReactive(target) {
return createReactiveObject(target, shallowReactiveMap, shallowReactiveHandlers)
}
const shallowReactiveHandlers = {
get: createGetter(false, true),
set
}
shallowReadonly
将get的isReadOnly、shallow入参同时设置为true
function shallowReadonly(target) {
return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers)
}
const shallowReadonlyHandlers = {
get: createGetter(true, true),
set(target, key) {
// readonly 的响应式对象不可以修改值
console.warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
);
return true;
},
};
isReactive
当获取IS_REACTIVE属性时,get方法中会返回!isReadOnly,非只读即为reactive
function isReactive(value) {
return !!value[IS_REACTIVE]
}
isReadonly
当获取IS_READONLY属性时,get方法中会返回isReadOnly
function isReadonly(value) {
return !!value[IS_READONLY]
}
isProxy
isProxy即为isReactive或者isReadonly
function isProxy(value) {
return isReactive(value) || isReadonly(value)
}
toRaw
toRaw的作用是获取原始对象,当我们调用toRaw方法时会去获取value[RAW],get方法中会判断该属性并获取相应的缓存数据,若是命中则返回原始数据(对应get中三目运算那部分)
function toRaw(value) {
const raw = value[RAW]
return raw ? toRaw(raw) : value
}
markRaw
markRaw为给数据加一个标识skip,createReactiveObject中判断命中skip时会立即返回原始对象,永远都不会走向代理
function markRaw(value) {
def(value, SKIP, true)
return value
}
结语
配合简化后代码看着更香