Vue3 源码解析系列 - watch(一)

238 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

前言

watch 是我们最常用的 api 之一,能够监听对象的修改,今天主要看看 watch 的源码是如何实现的。

使用

如果只监听一个数据源

// 监听 state.count 的 getter 方法,触发getter时触发watch
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 监听 ref 时,直接把 ref 传入
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

当然,有时候我们需要监听多个数据,如果每监听一个数据就写一个 watch 的话,会使代码变得更臃肿,所以 watch 也可以一次性监听 多个数据。

// 全部监听 ref
const ref1 = ref(0)
const ref2 = ref(0)
watch([ref1, ref2], ([ref1NewValue, ref2NewValue], [ref1OldValue, ref2OldValue]) => {
  console.log('ref1:', ref1NewValue, ref1OldValue);
  console.log('ref2:', ref2NewValue, ref2OldValue);
})

// ref 和 reactive可以一起监听
const ref1 = ref(0)
const state = reactive({ count: 0 })
watch([ref1, () => state.count], ([ref1NewValue, ref2NewValue], [ref1OldValue, ref2OldValue]) => {
  /** */
})

监听多个数据源时,回调函数回接收两个数组参数,第一个时新值集合,第二个时旧值集合,其中值的顺序和我们监听列表的顺序一致。

源码

学会了如何使用 watch ,我们再来看下 watch 的源码。watch 的源码位置在 packages/runtime-core/src/apiWatch.ts

// packages/runtime-core/src/apiWatch.ts
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
  source: T | WatchSource<T>,
  cb: any,
  options?: WatchOptions<Immediate>
): WatchStopHandle {
  return doWatch(source as any, cb, options)
}

可以看到, watch 接收3个参数,分别是监控源、回调函数、配置选项,然后直接反回 doWatch 的结果。
doWatch 函数的代码比较长,我们一部分一部分看。首先从声明看起。

function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect | object,
  cb: WatchCallback | null,
  { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
)

可以看到接收的参数与 watch 一致,监控源source,回调函数,配置参数。配置参数可以传5个

  • immediate 立即执行一次回调
  • deep 是否尝试检查深度嵌套对象或数组中的 property 变化
  • 如果需要在组件更新重新运行侦听器副作用,我们可以设置 flush 为 post,flush 选项还接受 sync,这将强制效果始终同步触发。然而这是低效的,应该很少需要。
  • 还有就是侦听器调试onTrack 和 onTrigger 方法,它们只在开发环境生效,其中- onTrack 将在响应式 property 或 ref 作为依赖项被追踪时被调用。onTrigger 将在依赖项变更导致副作用被触发时被调用。

这些就是 watch 的所以可选配置项。

小结

这节学了如何使用watch 方法,和一部分源码,下一篇我们再继续深入研究watch的源码。