最近工作不是很忙,從尤雨溪公布VUE3源碼以后团搞,就想著找個機(jī)會讀一下绞旅,也是比較忙摆尝,也是不太會TypeScript所以一直就擱置了,這段時間工作不是很忙因悲,趁著上班時間學(xué)了一天TypeScript堕汞,因?yàn)橛蠧#和JS基礎(chǔ),所以學(xué)起來也挺快的晃琳,然后就迫不及待的翻開了源碼讯检,emmmmm,這都是啥蝎土?视哑??
我水平不行誊涯,直接點(diǎn)開看的話挡毅,真的是,連猜帶懵暴构,猜這些個代碼是啥意思跪呈,這個模塊是干嘛的,看了一上午實(shí)在是看不下去了取逾,我受不了這種瞎撞的感覺耗绿,就在網(wǎng)上找一找有沒有大牛已經(jīng)讀過了,至少把結(jié)構(gòu)什么的能讓我搞懂砾隅,就找到了這個误阻,VUE3源碼導(dǎo)讀,在這里真的感謝這位老哥,讓我少走了很多彎路究反。
但是光知道結(jié)構(gòu)和一點(diǎn)點(diǎn)基礎(chǔ)寻定,看起來還是挺麻煩的,因?yàn)閂UE2到VUE3過渡實(shí)在是太大了精耐,用之前的定型思維去看VUE3的代碼還是有點(diǎn)難受狼速,這里我推薦先看RFC,看完以后對VUE的整個過渡會有一個大體的了解卦停,然后就可以快樂的閱讀源碼啦向胡。
我先看的是reactivity模塊的,這是一個極其重要的模塊惊完,它是一個數(shù)據(jù)響應(yīng)式系統(tǒng)僵芹。其暴露的主要 API 有 ref(數(shù)據(jù)容器)、reactive(基于 Proxy 實(shí)現(xiàn)的響應(yīng)式數(shù)據(jù))专执、computed(計(jì)算數(shù)據(jù))淮捆、effect(副作用) 等幾部分:
export {
reactive,
isReactive,
readonly,
isReadonly,
toRaw,
markReadonly,
markNonReactive
} from './reactive'
export {
computed,
ComputedRef,
WritableComputedRef,
WritableComputedOptions
} from './computed'
export {
effect,
stop,
pauseTracking,
resumeTracking,
ITERATE_KEY,
ReactiveEffect,
ReactiveEffectOptions,
DebuggerEvent
} from './effect'
export { lock, unlock } from './lock'
export { OperationTypes } from './operations'
我到現(xiàn)在只看了關(guān)于reactive的一些代碼,所以這篇文章也只會介紹一些reactive本股,關(guān)于其他的如ref,computed以后會慢慢進(jìn)行補(bǔ)充桐腌。
VUE3通過reactive()
來創(chuàng)建一個可觀察對象拄显,類似于Vue.observable()
,例如const obj = reactive({ count: 0 })
案站,
那么在調(diào)用reactive()之后到底做了什么呢躬审,我先把相關(guān)源碼貼出來
//reactive.ts
import { isObject, toRawType } from '@vue/shared'
import {
mutableHandlers,
readonlyHandlers,
shallowReadonlyHandlers
} from './baseHandlers'
import {
mutableCollectionHandlers,
readonlyCollectionHandlers
} from './collectionHandlers'
import { UnwrapRef, Ref } from './ref'
import { makeMap } from '@vue/shared'
// WeakMaps that store {raw <-> observed} pairs.
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()
const rawToReadonly = new WeakMap<any, any>()
const readonlyToRaw = new WeakMap<any, any>()
// WeakSets for values that are marked readonly or non-reactive during
// observable creation.
const readonlyValues = new WeakSet<any>()
const nonReactiveValues = new WeakSet<any>()
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
const isObservableType = /*#__PURE__*/ makeMap(
'Object,Array,Map,Set,WeakMap,WeakSet'
)
const canObserve = (value: any): boolean => {
return (
!value._isVue &&
!value._isVNode &&
isObservableType(toRawType(value)) &&
!nonReactiveValues.has(value)
)
}
// only unwrap nested ref
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
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 (readonlyToRaw.has(target)) {
return target
}
// target is explicitly marked as readonly by user
if (readonlyValues.has(target)) {
return readonly(target)
}
return createReactiveObject(
target,
rawToReactive,
reactiveToRaw,
mutableHandlers,
mutableCollectionHandlers
)
}
export function readonly<T extends object>(
target: T
): Readonly<UnwrapNestedRefs<T>> {
// value is a mutable observable, retrieve its original and return
// a readonly version.
if (reactiveToRaw.has(target)) {
target = reactiveToRaw.get(target)
}
return createReactiveObject(
target,
rawToReadonly,
readonlyToRaw,
readonlyHandlers,
readonlyCollectionHandlers
)
}
// @internal
// Return a reactive-copy of the original object, where only the root level
// properties are readonly, and does not recursively convert returned properties.
// This is used for creating the props proxy object for stateful components.
export function shallowReadonly<T extends object>(
target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
return createReactiveObject(
target,
rawToReadonly,
readonlyToRaw,
shallowReadonlyHandlers,
readonlyCollectionHandlers
)
}
function createReactiveObject(
target: unknown,
toProxy: WeakMap<any, any>,
toRaw: WeakMap<any, any>,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target already has corresponding Proxy
let observed = toProxy.get(target)
if (observed !== void 0) {
return observed
}
// target is already a Proxy
if (toRaw.has(target)) {
return target
}
// only a whitelist of value types can be observed.
if (!canObserve(target)) {
return target
}
const handlers = collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
observed = new Proxy(target, handlers)
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
export function isReactive(value: unknown): boolean {
return reactiveToRaw.has(value) || readonlyToRaw.has(value)
}
export function isReadonly(value: unknown): boolean {
return readonlyToRaw.has(value)
}
export function toRaw<T>(observed: T): T {
return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed
}
export function markReadonly<T>(value: T): T {
readonlyValues.add(value)
return value
}
export function markNonReactive<T>(value: T): T {
nonReactiveValues.add(value)
return value
}
很明顯,在使用reactive創(chuàng)建對象的時候蟆盐,先會判斷是否要創(chuàng)建的是一個只讀對象或者已經(jīng)被用戶明確的標(biāo)記只讀承边,然后調(diào)用createReactiveObject,進(jìn)行檢查石挂,是否要創(chuàng)建的對象是否已經(jīng)被定義成了一個proxy或者對象本身就是一個proxy博助,然后判斷這個對象是否可以被創(chuàng)建成一個proxy,然后接下來
const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
,這句就對要創(chuàng)建的proxy對象進(jìn)行判斷痹愚,選擇要提供陷阱函數(shù)富岳,這里分為baseHandlers和collectionHandlers,在這里我就不把源碼貼出來了拯腮,在創(chuàng)建完proxy之后窖式,就大功告成了,但是還是需要把創(chuàng)建好的proxy存下來动壤,以便下一次創(chuàng)建時進(jìn)行判斷
toProxy.set(target, observed)
toRaw.set(observed, target)
這就是reactive的核心了萝喘,還有一些關(guān)于readonly的方法在這里就不再一一贅述了,感興趣的可以深入進(jìn)行了解。