【vue3源码】十、响应式API中的工具函数
【vue3源码】十、响应式API中的工具函数
参考代码版本:vue 3.2.37
官方文档:https://vuejs.org/
isRef
export function isRef(r: any): r is Ref {
return !!(r && r.__v_isRef === true)
}通过对象中是否存在__v_isRef属性并且__v_isRef对应值为true来判断是否为ref。
unref
export function unref<T>(ref: T | Ref<T>): T {
return isRef(ref) ? (ref.value as any) : ref
}如果是ref则返回ref.value,否则直接返回ref
toRef
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
defaultValue?: T[K]
): ToRef<T[K]> {
const val = object[key]
return isRef(val)
? val
: (new ObjectRefImpl(object, key, defaultValue) as any)
}toRef接收三个参数:object待转换的对象、key待转换的key、defaultValue默认值。
如果object[key]是ref,则直接返回object[key]。否则返回一个ObjectRefImpl实例。
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K]
) {}
get value() {
const val = this._object[this._key]
return val === undefined ? (this._defaultValue as T[K]) : val
}
set value(newVal) {
this._object[this._key] = newVal
}
}在ObjectRefImpl构造器中会分别将object、key、defaultValue保存至自己的私有属性中,当获取ObjectRefImpl实例的value属性时,会从this._object中获取数据,由于this._object和原来的object内存地址是一致的,所以这和直接使用object获取key获取数据没有区别,只不过经过toRef转换之后,可以和ref那样,通过value属性进行取值、设值。
toRefs
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret
}toRefs中会声明一个新的对象或数组,然后遍历object的key值,并调用toRef,将结果存入新的对象或数组中,最后返回这个新的对象或数组。
isReactive
export function isReactive(value: unknown): boolean {
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW])
}
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}如果value是只读的,那么就对value的ReactiveFlags.RAW属性继续调用isReactive;否则根据value的ReactiveFlags.IS_REACTIVE属性判断是否为reactive。
isReadonly
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}通过value的ReactiveFlags.IS_READONLY属性判断是否只读。
isProxy
isProxy是用来判断value是否为reactive或readonly,并不是用来判断value是proxy类型的
export function isProxy(value: unknown): boolean {
return isReactive(value) || isReadonly(value)
}toRaw
获取传入对象的原始对象。
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
}observed的ReactiveFlags.RAW属性可以返回对象的原始对象,但这个原始对象有可能也是可以响应式对象(如readonly(reactive(obj))),所以递归调用toRaw,以获取真正的原始对象。
markRaw
将对象标记为永远不能转为reactive对象。
export function markRaw<T extends object>(
value: T
): T & { [RawSymbol]?: true } {
def(value, ReactiveFlags.SKIP, true)
return value
}通过Object.defineProperty将value的ReactiveFlags.SKIP(不会被遍历)属性标记为true。当尝试创建reactive时,会检查该值。
转载自:https://segmentfault.com/a/1190000042450212