轻云听书app
17.87MB · 2025-10-15
简化总结
Vue 3 的 watch
是响应式系统的高级功能之一,它用于侦听响应式数据的变化并执行回调。
其底层是基于 ReactiveEffect
的依赖追踪机制实现的。
源码位于 @vue/reactivity
包中。
import {
EMPTY_OBJ,
NOOP,
hasChanged,
isArray,
isFunction,
isMap,
isObject,
isPlainObject,
isSet,
remove,
} from '@vue/shared'
import { warn } from './warning'
import type { ComputedRef } from './computed'
import { ReactiveFlags } from './constants'
import {
type DebuggerOptions,
EffectFlags,
type EffectScheduler,
ReactiveEffect,
pauseTracking,
resetTracking,
} from './effect'
import { isReactive, isShallow } from './reactive'
import { type Ref, isRef } from './ref'
import { getCurrentScope } from './effectScope'
isRef
, isReactive
, isFunction
, isArray
等)ReactiveEffect
)hasChanged
、remove
)pauseTracking
/ resetTracking
)export enum WatchErrorCodes {
WATCH_GETTER = 2,
WATCH_CALLBACK,
WATCH_CLEANUP,
}
错误码 | 含义 |
---|---|
2 | 取值(getter)时出错 |
3 | 回调函数执行出错 |
4 | 清理函数执行出错 |
export type WatchEffect = (onCleanup: OnCleanup) => void
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
export type WatchCallback<V = any, OV = any> = (value: V, oldValue: OV, onCleanup: OnCleanup) => any
export type OnCleanup = (cleanupFn: () => void) => void
WatchEffect
:传入一个函数,自动执行并响应式追踪(即 watchEffect
)。WatchSource
:侦听的源,可以是 ref、computed 或函数。WatchCallback
:当源变化时的回调函数。export interface WatchOptions<Immediate = boolean> extends DebuggerOptions {
immediate?: Immediate
deep?: boolean | number
once?: boolean
scheduler?: WatchScheduler
onWarn?: (msg: string, ...args: any[]) => void
augmentJob?: (job: (...args: any[]) => void) => void
call?: (
fn: Function | Function[],
type: WatchErrorCodes,
args?: unknown[],
) => void
}
immediate
: 是否立即执行一次回调。deep
: 是否深度侦听。once
: 是否只触发一次。scheduler
: 自定义调度函数(可实现 flush 机制)。augmentJob
: 用于增强 job(Vue 内部使用)。call
: 自定义调用函数(可注入错误处理逻辑)。const cleanupMap: WeakMap<ReactiveEffect, (() => void)[]> = new WeakMap()
let activeWatcher: ReactiveEffect | undefined = undefined
ReactiveEffect
对应的清理函数。onWatcherCleanup
)。export function getCurrentWatcher(): ReactiveEffect<any> | undefined {
return activeWatcher
}
export function onWatcherCleanup(
cleanupFn: () => void,
failSilently = false,
owner: ReactiveEffect | undefined = activeWatcher,
): void {
if (owner) {
let cleanups = cleanupMap.get(owner)
if (!cleanups) cleanupMap.set(owner, (cleanups = []))
cleanups.push(cleanupFn)
} else if (__DEV__ && !failSilently) {
warn(
`onWatcherCleanup() was called when there was no active watcher`
)
}
}
export function watch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb?: WatchCallback | null,
options: WatchOptions = EMPTY_OBJ,
): WatchHandle {
const { immediate, deep, once, scheduler, augmentJob, call } = options
source
: 侦听的目标(可以是 ref、reactive、函数、数组)。cb
: 回调函数,如果不存在,则视为 watchEffect
。options
: 配置项。const warnInvalidSource = (s: unknown) => {
;(options.onWarn || 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.`,
)
}
reactiveGetter
const reactiveGetter = (source: object) => {
if (deep) return source
if (isShallow(source) || deep === false || deep === 0)
return traverse(source, 1)
return traverse(source)
}
这一段是 watch 的核心逻辑之一:根据不同类型的 source,创建合适的 getter。
if (isRef(source)) {
getter = () => source.value
forceTrigger = isShallow(source)
} else if (isReactive(source)) {
getter = () => reactiveGetter(source)
forceTrigger = true
} else if (isArray(source)) {
isMultiSource = true
...
} else if (isFunction(source)) {
...
} else {
getter = NOOP
__DEV__ && warnInvalidSource(source)
}
isRef
: 直接访问 .value
。isReactive
: 通过 traverse
收集依赖。isArray
: 多源 watch。isFunction
: 区分 watch(source, cb)
与 watchEffect(source)
。if (cb && deep) {
const baseGetter = getter
const depth = deep === true ? Infinity : deep
getter = () => traverse(baseGetter(), depth)
}
effect = new ReactiveEffect(getter)
ReactiveEffect
是 Vue 响应式系统的核心执行单元,负责依赖收集与更新调度。
const job = (immediateFirstRun?: boolean) => {
if (!effect.dirty && !immediateFirstRun) return
if (cb) {
const newValue = effect.run()
if (deep || forceTrigger || hasChanged(newValue, oldValue)) {
if (cleanup) cleanup()
const currentWatcher = activeWatcher
activeWatcher = effect
try {
cb(newValue, oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue, boundCleanup)
oldValue = newValue
} finally {
activeWatcher = currentWatcher
}
}
} else {
// watchEffect
effect.run()
}
}
effect.scheduler = scheduler
? () => scheduler(job, false)
: (job as EffectScheduler)
初始化:
if (cb) {
if (immediate) job(true)
else oldValue = effect.run()
} else if (scheduler) {
scheduler(job.bind(null, true), true)
} else {
effect.run()
}
watchHandle.pause = effect.pause.bind(effect)
watchHandle.resume = effect.resume.bind(effect)
watchHandle.stop = watchHandle
return watchHandle
pause()
: 暂停依赖收集。resume()
: 恢复依赖收集。stop()
: 停止监听。traverse
函数(深度遍历)export function traverse(
value: unknown,
depth: number = Infinity,
seen?: Map<unknown, number>,
): unknown {
if (depth <= 0 || !isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
return value
}
seen = seen || new Map()
if ((seen.get(value) || 0) >= depth) {
return value
}
seen.set(value, depth)
depth--
if (isRef(value)) {
traverse(value.value, depth, seen)
} else if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], depth, seen)
}
} else if (isSet(value) || isMap(value)) {
value.forEach(v => traverse(v, depth, seen))
} else if (isPlainObject(value)) {
for (const key in value) traverse(value[key], depth, seen)
for (const key of Object.getOwnPropertySymbols(value)) {
if (Object.prototype.propertyIsEnumerable.call(value, key)) {
traverse(value[key as any], depth, seen)
}
}
}
return value
}
watch
的核心思想:source
类型创建 getter。ReactiveEffect
追踪依赖。job
,触发回调。immediate
、deep
、once
等灵活配置。traverse
实现深层依赖收集。onWatcherCleanup
实现副作用清理机制。特性 | watch | watchEffect |
---|---|---|
参数 | 源 + 回调 | 仅副作用函数 |
取值方式 | 比较新旧值 | 自动执行函数内部依赖 |
是否传参 | 需要指定 source | 自动收集依赖 |
使用场景 | 精确侦听某个数据变化 | 响应式副作用逻辑 |
若要深入理解 watch
:
ReactiveEffect
的实现;track
/ trigger
;traverse
如何触发深度依赖;scheduler
如何与 flush
配合。本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。