【vue3源碼】十显蝌、響應(yīng)式API中的工具函數(shù)
參考代碼版本: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
對應(yīng)值為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
接收三個參數(shù):object
待轉(zhuǎn)換的對象、key
待轉(zhuǎn)換的key
招盲、defaultValue
默認(rèn)值低缩。
如果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
構(gòu)造器中會分別將object
、key
顶籽、defaultValue
保存至自己的私有屬性中玩般,當(dāng)獲取ObjectRefImpl
實例的value
屬性時,會從this._object
中獲取數(shù)據(jù)礼饱,由于this._object
和原來的object
內(nèi)存地址是一致的坏为,所以這和直接使用object
獲取key
獲取數(shù)據(jù)沒有區(qū)別,只不過經(jīng)過toRef
轉(zhuǎn)換之后镊绪,可以和ref
那樣匀伏,通過value
屬性進行取值、設(shè)值蝴韭。
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
中會聲明一個新的對象或數(shù)組够颠,然后遍歷object
的key
值,并調(diào)用toRef
榄鉴,將結(jié)果存入新的對象或數(shù)組中履磨,最后返回這個新的對象或數(shù)組。
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
屬性繼續(xù)調(diào)用isReactive
剃诅;否則根據(jù)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
屬性可以返回對象的原始對象位岔,但這個原始對象有可能也是可以響應(yīng)式對象(如readonly(reactive(obj))
)如筛,所以遞歸調(diào)用toRaw
堡牡,以獲取真正的原始對象抒抬。
markRaw
將對象標(biāo)記為永遠(yuǎn)不能轉(zhuǎn)為reactive
對象。
export function markRaw<T extends object>(
value: T
): T & { [RawSymbol]?: true } {
def(value, ReactiveFlags.SKIP, true)
return value
}
通過Object.defineProperty
將value
的ReactiveFlags.SKIP
(不會被遍歷)屬性標(biāo)記為true
晤柄。當(dāng)嘗試創(chuàng)建reactive
時擦剑,會檢查該值。