在 Vue3 中,watch 是非常核心的响应式工具,但很多人只停留在基础用法,对它的配置项理解不够深入。
这篇文章带你一次性搞清楚 watch 的几个关键参数:
flushdeepimmediate
并给出实际使用建议。
一、基础回顾
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。