“ 本文正在参加「金石计划 . 瓜分6万现金大奖」 ”
watch 在 vue 中使用
watch 本质就是一个响应式数据,当数据发生变化的时候,执行相应的回调函数。
watch(obj, ()=>{
console.log(123)
})
// 当监测到数据变化时,会重新执行 watch 的回调函数
- watch 需要监听特定的数据源,在第一次加载时不运行函数,只有当监听的数据源变化时才会运行
为了根据响应式状态自动应用和重新应用副作用,我们还可以使用 watchEffect 函数。它会立即执行传入的一个函数,同时响应式追踪依赖,并在依赖更新时重新运行该函数。
- 可以看出 watchEffect 函数不需要传入一个数据源,只需传入一个函数,在函数中使用到的响应式数据,vue 都会追踪依赖,当数据改变时,watchEffect 会再次运行。
- 与 watch 不同的是,watchEffect 在页面第一次加载的时候就会执行
源码分析
在 Vue2中watch 是 option 写法中一个很常用的选项,使用它可以非常方便的监听一个数据源的变化,而在 Vue3 中watch 独立成了一个 响应式api。
watchEffect
watchEffect 的许多行为与 watch 一致,我们来看他的实现:
// 首先来看参数类型
export type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void
export interface WatchOptionsBase extends DebuggerOptions {
flush?: 'pre' | 'post' | 'sync'
}
export type WatchStopHandle = () => void
export function watchEffect(
effect: WatchEffect, // 接收函数类型的变量,并且在这个函数中会传入 onInvalidate 参数,用以清除副作用
options?: WatchOptionsBase // 在这个对象中有三个属性,你可以修改 flush 来改变副作用的刷新时机,默认为 pre,当修改为 post 时,就可以在组件更新后触发这个副作用侦听器,改同 sync 会强制同步触发。而 onTrack 和 onTrigger 选项可以用于调试侦听器的行为,并且两个参数只能在开发模式下工作。
): WatchStopHandle {
return doWatch(effect, null, options)
}
watch
当我们调用 watch 的时候,实际上是调用的 runtime-core/apiWatch.ts 中的 watch 方法:
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.`
)
}
return doWatch(source as any, cb, options)
}
watch 接收 3 个参数,source 侦听的数据源,cb 回调函数,options 侦听选项。
(1)source 参数
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
type MultiWatchSources = (WatchSource<unknown> | object)[]
从两个类型定义看出,数据源支持传入单个的 Ref、Computed 响应式对象,或者传入一个返回相同泛型类型的函数,以及 source 支持传入数组,以便能同时监听多个数据源。
(2)cb 参数
在这个最通用的声明中,cb 的类型是 any,但 cb 这个回调函数也有类型:
export type WatchCallback<V = any, OV = any> = (
value: V,
oldValue: OV,
onInvalidate: InvalidateCbRegistrator
) => any
在回调函数中,会提供最新的 value、旧 value,以及 onInvalidate 函数用以清除副作用。
(3)options
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
immediate?: Immediate
deep?: boolean
}
可以看到 options 的类型 WatchOptions 继承了 WatchOptionsBase。
分析完参数后,可以看到函数体内的逻辑与 watchEffect 几乎一致,但是多了在开发环境下检测回调函数是否是函数类型,如果回调函数不是函数,就会报警。执行 doWatch 时的传参与 watchEffect 相比,多了第二个参数回调函数。