watch源码浅析
通过对reactive的解读中明白了在vue中将被响应式系统收集的依赖为组件更新,computed,watch三类。而watch的具体实现具体是怎样的?这就是本次学习的目标。
依然通过暴露的composition api入手,从watch方法看整个watch的实现过程。
一开始就是连续的4个函数重载,就知道vue3中的watch肯定也还是有所变化的了。
// apiWatch.ts
// overload #1: array of multiple sources + cb
export function watch<
T extends MultiWatchSources,
Immediate extends Readonly<boolean> = false
>(
sources: [...T],
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
options?: WatchOptions<Immediate>
): WatchStopHandle
// overload #2 for multiple sources w/ `as const`
// watch([foo, bar] as const, () => {})
// somehow [...T] breaks when the type is readonly
export function watch<
T extends Readonly<MultiWatchSources>,
Immediate extends Readonly<boolean> = false
>(
source: T,
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
options?: WatchOptions<Immediate>
): WatchStopHandle
// overload #2: single source + cb
export function watch<T, Immediate extends Readonly<boolean> = false>(
source: WatchSource<T>,
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
options?: WatchOptions<Immediate>
): WatchStopHandle
// overload #3: watching reactive object w/ cb
export function watch<
T extends object,
Immediate extends Readonly<boolean> = false
>(
source: T,
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
options?: WatchOptions<Immediate>
): WatchStopHandle
从以上的函数重载可以知道watch方法可以为多数据源,单一数据源提供观测。并且数据源应该是vue3中的Ref实例,ComputedRef实例,或者effect函数。
// apiWatch.ts
// implementation
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
source: T | WatchSource<T>,
cb: any,
options?: WatchOptions<Immediate>
): WatchStopHandle {
// dev环境下,传入的cb不是函数就输出提示
if (__DEV__ && !isFunction(cb)) {
warn(
`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
`Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
`supports \`watch(source, cb, options?) signature.`
)
}
return doWatch(source as any, cb, options)
}
// 转换watch的过程比较长,就截取一些关键内容,以下代码非完整源码
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb: WatchCallback | null,
{
immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ,
instance = currentInstance
): WatchStopHandle {
// dev环境下的部分提示
if (__DEV__ && !cb) {
// 打印提示的代码
}
// 提示非法数据源,合法的数据源是:getter/effect function, ref, reactive object
const warnInvalidSource = (s: unknown) => {
warn(
`Invalid watch source: `,
s,
`A watch source can only be a getter/effect function, a ref, ` +
`a reactive object, or an array of these types.`
)
}
// 以getter函数获取数据源的值
let getter: () => any
let forceTrigger = false
if (isRef(source)) {
// 数据源是ref
getter = () => (source as Ref).value
forceTrigger =