likes
comments
collection
share

重学 Vue 之侦听器

作者站长头像
站长
· 阅读数 14

watch 和 watchEffect

API 文档的定义很贴近的说明了两个函数的功能,watch 函数侦听一个或多个数据源,在数据变化后执行回调函数,watchEffect 函数立即执行一个回调函数,同时响应式追踪其依赖,并在依赖变化时重新执行。

// 侦听单个来源
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
}

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

对比两个函数的类型定义,可以清晰的看出它们的共性和区别,这一节主要说明它们的共性。watch 和 watchEffect 都有 flush,onTrack,onTrigger,StopHandle,下面主要分析 flush 选项和 stopHandle 函数。

watch 函数和 watchEffect 函数的回调中只能获取到 Vue 模版中更新前的数据,使用 flush:'post' 可以获取到 Vue 模版中更新后的数据。在下面的例子中,当未使用 flush 选项的时候,watch 函数和 watchEffect 函数只能获取到 x.count 更新前的值,使用 flush:'post' 后才会获取到 x.count 更新后的值。

重学 Vue 之侦听器

在某些特殊场合,需要停止 watch 函数和 watchEffect 的侦听功能,这个时候可以调用它们返回的回调函数。继续使用上文的例子,这里增加了一个 stop 按钮,点击后会调用 watch 和 watchEffect 返回的回调函数。之后再点击 add 按钮,可以发现 watchInfo 和 watchEffectInfo 的值没有发生变化。

重学 Vue 之侦听器

watch 的特殊性

上一节主要分析了 watch 和 watchEffect 的共性,这里分析了 watch 的特殊性,包括 WatchSource,immediate,deep。

由于 watch 需要明确的数据源才能进行侦听,所以有了支持多种类型的 WatchSource,它包括 ref ,响应式对象,函数,和以上类型组成的数组。

watch 函数不会立即侦听数据,而是会在数据变化后调用回调函数,使用 immediate:true 可以实现立即侦听数据。以下例子中 watchInfo 刚开始时没有数据的,而 watchEffectInfo 刚开始就有数据,当 watch 函数使用 immediate 选项的时候 watchInfo 才可以在初始化的时候就有数据

重学 Vue 之侦听器

对于深层对象,watch 无法侦听到,需要使用 deep:true 才可以侦听到深层对象的数据变化。以下例子中,watch 侦听了一个返回响应式对象的函数,当响应式对象里面的值发生变化后 watchInfo 的值并没有发生变化,而 watchEffectInfo 的值发生了变化。如果需要 watchInfo 的值和 watchEffectInfo 的值保持一致,watch 函数就需要使用 deep 选项。

重学 Vue 之侦听器

结论

理解侦听器的概念时,可以直接将 watch 函数和 watchEffect 函数进行对比,找出它们的共性和特殊性。首先是共性,它们都有侦听数据的效果,可以使用 flush:true 让回调函数获取到 Vue 更新后的数据,使用返回的回调函数来停止侦听。其次是 watch 的特殊性,它有明确的侦听数据源,不会立即进行侦听,无法侦听到深层对象。

参考资料:cn.vuejs.org