Vue3 watch 高级用法总结

5 阅读2分钟

在 Vue3 中,watch 是非常核心的响应式工具,但很多人只停留在基础用法,对它的配置项理解不够深入。

这篇文章带你一次性搞清楚 watch 的几个关键参数:

  • flush
  • deep
  • immediate

并给出实际使用建议。


一、基础回顾

watch(source, callback, options)
  • source:监听的响应式数据
  • callback:变化时执行
  • options:高级控制参数(本文重点)

二、flush:决定回调执行时机(很关键)

flush?: 'pre' | 'post' | 'sync'

1️⃣ pre(默认)

flush: 'pre'
  • 在组件更新前执行
  • 默认行为

👉 特点:

  • 可以拿到旧 DOM
  • 常用于数据联动

2️⃣ post

flush: 'post'

👉 在 DOM 更新后执行

👉 适合场景:

  • 操作 DOM(scroll、focus)
  • 依赖最新 UI 状态
watch(() => props.options, () => {
  // DOM 已经更新
  scrollToNode()
}, { flush: 'post' })

3️⃣ sync(慎用)

flush: 'sync'

👉 同步执行(不进入更新队列)

特点:

  • 立即执行
  • 不等 nextTick

⚠️ 风险:

  • 可能触发多次重复执行
  • 容易造成性能问题

👉 使用场景:

  • 必须同步(极少数情况)
  • 比如底层状态桥接

三、deep:深度监听

deep: true

👉 用于监听对象/数组内部变化

watch(() => obj, callback, { deep: true })

否则:

obj.a = 1 // ❌ 不会触发

开启后:

obj.a = 1 // ✅ 会触发

⚠️ 注意

  • 性能开销较大(递归监听)
  • 不推荐滥用

👉 更优方案:

watch(() => obj.a, callback)

👉 精准监听 > deep


四、immediate:是否立即执行

immediate: true

👉 在初始化时立即执行一次

watch(
  () => props.id,
  (id) => {
    fetchData(id)
  },
  { immediate: true }
)

👉 等价于:

  • mounted + watch 的合体

五、组合使用示例

watch(
  () => props.options,
  () => {
    syncTreeFromStore()
  },
  {
    flush: 'post',   // 等 DOM 更新
    deep: true,      // 深度监听
    immediate: true, // 初始化执行
  }
)

六、实际建议(重点)

✅ 1. 默认用法

watch(source, callback)

✅ 2. 涉及 DOM

flush: 'post'

✅ 3. 初始化需要执行

immediate: true

❌ 4. 慎用 deep

优先:

watch(() => obj.xxx)

而不是:

deep: true

❌ 5. 尽量避免 sync

除非你非常确定你在做什么。


七、一个更本质的理解(给进阶用)

你可以这样理解:

参数本质控制
flush调度时机(调度器)
deep依赖收集范围
immediate是否触发首次执行

👉 本质就是:

watch = 依赖收集 + 调度执行


八、总结

一句话总结:

flush 控时间,deep 控范围,immediate 控是否立即执行


如果你写复杂交互(比如树组件、表格状态恢复),
真正决定你写对没的,其实是 flush