jk's notes
  • Vue3 中 watch 的使用

Vue3 中 watch 的使用

watch 用于在数据 (监听源) 变化时, 触发某个行为 (调用一个函数), 并获得更新的数据 (附带原有的数据).

相关的 watch 方法有:

  • watch() 公共方法
  • watchEffect() 自动跟踪源
  • watchPostEffect() 自动跟踪后置执行
  • watchSyncEffect() 自动跟踪实时执行

watch 是最为原始的方法, watchEffect 算是一种优化改进, 会如同 计算属性一样, 自动配置需要跟踪的源. 剩下的 *Effect 则是 watchEffect 的语法糖 (快捷方法).

1. watch 语法

官方文档: https://cn.vuejs.org/api/reactivity-core.html#watch

基本用法:

const stopHandle = watch(响应式字段 | () => xxx, (newVal, oldVal) => {
  // ...
})

当监控的字段发生变化时, 回调函数会被执行. 并将新值, 与旧值作为参数传入, 以供开发者实现具体任务.

补充说明:

  1. 源可以不是一个字段, 可以是一个数组, 或元组, 也可以是一个 getter.
  2. 回调函数可以有第三个参数, 用于频繁调用响应时, 取消上一次未完成的任务.
  3. watch 只会监听源的变化.
  4. 要停止监听, 只需要调用其返回的函数, 例如: stopHandle(). 同步执行的 watch 会自动处理停止, 只有异步定义的 watch 才需要手动停止.
  5. WatchOptions 定义了附加操作:
    • watch 是惰性执行的, 如果要立即执行, 使用选项 immediate: true
    • 源是对象时, 默认只会监听表层属性, 如果要递归所有属性, 使用 deep: true, 但会影响性能
    • 回调函数会在 DOM 刷新前进行调用, 如果要在 DOM 刷新后调用, 使用 flush: 'post', 若要实时则用 'flush: sync'
    • onTrack 和 onTrigger 在调试模式下执行, 用于监视调用过程.

DOM 刷新之前, 意味着此时访问 DOM, 获得的是更新之前的状态.

watch 完整语法:

// 侦听单个来源
function watch<T>(
  source: WatchSource<T>,
  callback: WatchCallback<T>,
  options?: WatchOptions
): StopHandle

// 侦听多个来源
function watch<T>(
  sources: WatchSource<T>[],
  callback: WatchCallback<T[]>,
  options?: WatchOptions
): StopHandle

type WatchCallback<T> = (
  value: T,
  oldValue: T,
  onCleanup: (cleanupFn: () => void) => void
) => void

type WatchSource<T> =
  | Ref<T> // ref
  | (() => T) // getter
  | T extends object
  ? T
  : never // 响应式对象

interface WatchOptions extends WatchEffectOptions {
  immediate?: boolean // 默认:false
  deep?: boolean // 默认:false
  flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

2. watchEffect 语法

watch 需要设置源, 再定义回调函数, 为了简化, watchEffect 会自动跟踪源, 逻辑上类似于 计算属性的处理方式.

一般用法:

watchEffect(() => {
  // ... 使用到的数据会自动绑定称为源, 一旦源发生变化, 就会触发该方法
})

执行上的注意点:

  1. watchEffect 默认是 immediate, 有别于 watch 的惰性执行.
  2. watchEffect 中或含有异步代码, 仅有同步代码中使用到的依赖会被作为源使用.
  3. watchEffect 会自动设定需要跟踪的依赖, 并且默认就是跟踪使用到属性, 不存在需要使用 deep 的情况.
  4. 相比较 watch, watch 控制更清晰, 但需要一个个手动输入; watchEffect 更简洁, 但依赖不那么清晰.
  5. watch 不因回调时的代码而影响源, watchEffect 则依赖回调代码中的数据来确定源.
  6. 使用上默认依旧会在 DOM 更新前执行, 如果修改, 使用 flush 选项. 或两个语法糖.

严格定义:

function watchEffect(
  effect: (onCleanup: OnCleanup) => void,
  options?: WatchEffectOptions
): StopHandle

type OnCleanup = (cleanupFn: () => void) => void

interface WatchEffectOptions {
  flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

type StopHandle = () => void

3. watchPostEffect 和 watchSyncEffect

相当于 flush 分别设置为 post 和 sync 的 watchEffect.

Last Updated:
Contributors: jk