最近工作不是很忙,从尤雨溪公布VUE3源码以后,就想着找个机会读一下,也是比较忙,也是不太会TypeScript所以一直就搁置了,这段时间工作不是很忙,趁着上班时间学了一天TypeScript,因为有C#和JS基础,所以学起来也挺快的,然后就迫不及待的翻开了源码,emmmmm,这都是啥???
我水平不行,直接点开看的话,真的是,连猜带懵,猜这些个代码是啥意思,这个模块是干嘛的,看了一上午实在是看不下去了,我受不了这种瞎撞的感觉,就在网上找一找有没有大牛已经读过了,至少把结构什么的能让我搞懂,就找到了这个,VUE3源码导读,在这里真的感谢这位老哥,让我少走了很多弯路。
但是光知道结构和一点点基础,看起来还是挺麻烦的,因为VUE2到VUE3过渡实在是太大了,用之前的定型思维去看VUE3的代码还是有点难受,这里我推荐先看RFC,看完以后对VUE的整个过渡会有一个大体的了解,然后就可以快乐的阅读源码啦。
我先看的是reactivity模块的,这是一个极其重要的模块,它是一个数据响应式系统。其暴露的主要 API 有 ref(数据容器)、reactive(基于 Proxy 实现的响应式数据)、computed(计算数据)、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'
我到现在只看了关于reactive的一些代码,所以这篇文章也只会介绍一些reactive,关于其他的如ref,computed以后会慢慢进行补充。
VUE3通过reactive()
来创建一个可观察对象,类似于Vue.observable()
,例如const obj = reactive({ count: 0 })
,
那么在调用reactive()之后到底做了什么呢,我先把相关源码贴出来
//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创建对象的时候,先会判断是否要创建的是一个只读对象或者已经被用户明确的标记只读,然后调用createReactiveObject,进行检查,是否要创建的对象是否已经被定义成了一个proxy或者对象本身就是一个proxy,然后判断这个对象是否可以被创建成一个proxy,然后接下来
const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
,这句就对要创建的proxy对象进行判断,选择要提供陷阱函数,这里分为baseHandlers和collectionHandlers,在这里我就不把源码贴出来了,在创建完proxy之后,就大功告成了,但是还是需要把创建好的proxy存下来,以便下一次创建时进行判断
toProxy.set(target, observed)
toRaw.set(observed, target)
这就是reactive的核心了,还有一些关于readonly的方法在这里就不再一一赘述了,感兴趣的可以深入进行了解。