export function watch<T = any, Immediate extends Readonly<boolean> = false>(
source: T | WatchSource<T>,
cb: any,
options?: WatchOptions<Immediate>
): WatchStopHandle {
// 在开发环境下,如果回调函数不是函数,则发出警告
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.`
)
}
// 调用 doWatch 函数来执行监视操作
return doWatch(source as any, cb, options)
}
这段代码是 watch
函数的一个重载形式,用于在开发环境下检查回调函数的类型,并调用 doWatch
函数来实际执行监视操作。
解释:
这段代码导出了一个名为 watch
的函数,它接受三个参数:source
、cb
和 options
。source
表示要监视的数据源,cb
表示回调函数,options
表示监视的配置选项。
在开发环境下,通过 __DEV__
条件判断检查回调函数 cb
是否是一个函数。如果不是函数,则发出警告,提示用户 watch(fn, options?)
的签名已经被移动到单独的 API,建议使用 watchEffect(fn, options?)
代替。
最后,调用 doWatch
函数来执行监视操作,并返回一个 WatchStopHandle
函数,用于停止监视。
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb: WatchCallback | null,
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
// 在开发环境下,如果没有提供回调函数 cb,但提供了 immediate 或 deep 选项,
// 则发出警告,因为这些选项只对特定的签名有效。
if (__DEV__ && !cb) {
if (immediate !== undefined) {
warn(
`watch() "immediate" option is only respected when using the ` +
`watch(source, callback, options?) signature.`
)
}
if (deep !== undefined) {
warn(
`watch() "deep" option is only respected when using the ` +
`watch(source, callback, options?) signature.`
)
}
}
// 定义用于警告无效监视源的函数
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.`
)
}
// 获取当前组件实例
const instance =
getCurrentScope() === currentInstance?.scope ? currentInstance : null
let getter: () => any
let forceTrigger = false
let isMultiSource = false
// 根据不同类型的数据源,设置相应的 getter 和 forceTrigger 值
// 如果是 ref 对象
if (isRef(source)) {
// 创建 getter 并读取 ref 对象的 value
getter = () => source.value
forceTrigger = isShallow(source)
} else if (isReactive(source)) {
// 如果是 reactive 对象,直接返回 getter 函数,设置 deep 为 true
getter = () => source
deep = true
} else if (isArray(source)) {
// 如果是数组,遍历数组处理里面的每个元素
isMultiSource = true
forceTrigger = source.some(s => isReactive(s) || isShallow(s))
getter = () =>
source.map(s => {
if (isRef(s)) {
return s.value
} else if (isReactive(s)) {
// traverse 就是一个递归,相当于深度监听
return traverse(s)
} else if (isFunction(s)) {
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
} else {
__DEV__ && warnInvalidSource(s)
}
})
} else if (isFunction(source)) {
if (cb) {
// getter with cb,即提供了回调函数 cb
getter = () =>
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
} else {
// no cb -> simple effect,即未提供回调函数 cb
getter = () => {
if (instance && instance.isUnmounted) {
return
}
if (cleanup) {
cleanup()
}
return callWithAsyncErrorHandling(
source,
instance,
ErrorCodes.WATCH_CALLBACK,
[onCleanup]
)
}
}
} else {
getter = NOOP // 无效的监视源,设置 getter 为 NOOP 函数
__DEV__ && warnInvalidSource(source)
}
// 如果开启了 2.x 兼容性选项且提供了回调函数 cb 且 deep 为 false,则添加兼容性支持
if (__COMPAT__ && cb && !deep) {
const baseGetter = getter
getter = () => {
const val = baseGetter()
if (
isArray(val) &&
checkCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance)
) {
traverse(val)
}
return val
}
}
// 如果设置了 deep 选项,对 getter 的返回值进行递归深度监听 traverse
if (cb && deep) {
const baseGetter = getter
getter = () => traverse(baseGetter())
}
let cleanup: () => void
let onCleanup: OnCleanup = (fn: () => void) => {
cleanup = effect.onStop = () => {
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
}
}
// 在服务器端渲染 (SSR) 模式下,不需要设置实际的 effect,它应该是无操作的,除非设置了 eager 或 sync flush 选项
let ssrCleanup: (() => void)[] | undefined
if (__SSR__ && isInSSRComponentSetup) {
// 在 SSR 中,不会调用 invalidate 回调函数(+ runner 未设置)
onCleanup = NOOP
if (!cb) {
getter() // 在 SSR 情况下立即获取数据
} else if (immediate) {
// 在 SSR 情况下,如果设置了 immediate 选项,则立即调用回调函数 cb
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
getter(),
isMultiSource ? [] : undefined,
onCleanup
])
}
if (flush === 'sync') {
// 在 SSR 情况下,如果设置了 sync flush,则获取 SSR 上下文,并将 cleanup 函数添加到 SSR 清理句柄中
const ctx = useSSRContext()!
ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = [])
} else {
return NOOP // 其他情况下,返回无操作函数
}
}
// 初始化 oldValue,根据数据源是否为多源(数组)进行初始化
let oldValue: any = isMultiSource
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
: INITIAL_WATCHER_VALUE
// 定义要运行的任务(job)函数,根据 cb 是否存在执行不同逻辑
const job: SchedulerJob = () => {
if (!effect.active) {
return
}
if (cb) {
// watch(source, cb) 情况
const newValue = effect.run()
if (
deep ||
forceTrigger ||
(isMultiSource
? (newValue as any[]).some((v, i) => hasChanged(v, oldValue[i]))
: hasChanged(newValue, oldValue)) ||
(__COMPAT__ &&
isArray(newValue) &&
isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))
) {
// 在运行回调函数 cb 之前清理
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue,
// 当首次变化时(INITIAL_WATCHER_VALUE 为 {})将旧值传递为 undefined
oldValue === INITIAL_WATCHER_VALUE ? undefined : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, onCleanup
])
// 彩蛋!!基础笔记中:`如果是引用类型(比如直接监听最外层 message)(此时已开启深度监听),则返回的新值和旧值是一样的(深层也是一样的,都是新值的数值)`
// 原因就在此:直接将引用(地址)赋值,深层也必定一样
oldValue = newValue
}
} else {
// watchEffect 情况
effect.run()
}
}
// 重要:将任务标记为 watcher 回调,以便调度器知道可以自行触发它 (#1727)
job.allowRecurse = !!cb
// 执行不同调度
let scheduler: EffectScheduler
// 根据 flush 选项设置调度器
if (flush === 'sync') {
scheduler = job as any // 直接调用任务函数
} else if (flush === 'post') {
// queuePostRenderEffect 组件更新之后执行
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {
// 默认情况下使用 'pre'
job.pre = true
if (instance) job.id = instance.uid
scheduler = () => queueJob(job)
}
// 创建响应式 effect
const effect = new ReactiveEffect(getter, scheduler)
if (__DEV__) {
effect.onTrack = onTrack
effect.onTrigger = onTrigger
}
// 初始化运行任务
if (cb) {
if (immediate) {
job() // 立即运行任务
} else {
oldValue = effect.run() // 初始化 oldValue
}
} else if (flush === 'post') {
// 如果是 watchEffect,且 flush 为 'post',则将任务添加到 post-render 队列中执行
queuePostRenderEffect(
effect.run.bind(effect),
instance && instance.suspense
)
} else {
effect.run() // 初始化运行任务
}
// 定义停止监视的函数
const unwatch = () => {
effect.stop() // 停止 effect
if (instance && instance.scope) {
remove(instance.scope.effects!, effect) // 从组件实例中移除 effect
}
}
// 在 SSR 情况下,将 cleanup 函数添加到清理句柄中
if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
// 返回停止监视的函数
return unwatch
}
上述代码实现了watch
的核心逻辑。
它根据不同的数据源类型,设置不同的getter
函数,然后在数据源发生变化时执行回调函数或效果函数。它还支持immediate
、deep
、flush
等选项,以及在服务器端渲染(SSR)环境下的特殊处理。
这段代码的核心是创建一个响应式effect
,并在需要时执行回调函数或效果函数,以实现数据的监视和响应。