✅ 第 10 题:watch VS watchEffect 的区别是什么?触发时机有什么不同?
🎯 一、标准回答(你在面试应该先说的)
✅ 1. watch:显式侦听某个特定数据源
- 需要指定侦听对象
- 默认 懒执行(lazy) ,只有依赖发生变化才触发
- 获取新旧值 (
newValue, oldValue) - 可控制
flush时机
示例:
javascript
watch(count, (newVal, oldVal) => {
console.log('count changed')
})
✅ 2. watchEffect:自动收集依赖,不需要指定监听对象
- 未指定具体依赖,运行时自动收集依赖数据
- 默认 立即执行一次(立即执行模式)
- 获取不到旧值
- 更适合副作用逻辑(比如接口、localStorage)
示例:
scss
watchEffect(() => {
console.log(count.value)
})
🎯 二、触发时机(超关键)
Vue3 的侦听器有 3 种触发时机(flush):
| 模式 | 时机 | 特点 |
|---|---|---|
pre(默认) |
组件渲染前触发(同步) | 不保证 DOM 最新 |
post |
组件 DOM 更新后触发 | 可安全访问最新 DOM |
sync |
同步触发 | 更危险,一般不用 |
watch 和 watchEffect 的默认时机不同:
🔹 watch 默认:flush: 'pre'
- 不保证 DOM 已更新
- 一般用于数据逻辑,不用于 DOM 操作
🔹 watchEffect 默认:也是 flush: 'pre',但它会立即执行一次
- 所以表现上像是"自动"、"立即"
🎯 三、最关键的区别(面试官想听的 4 点)
| 项目 | watch | watchEffect |
|---|---|---|
| 依赖收集 | 手动指定 | 自动分析依赖 |
| 何时执行 | 默认仅数据变化触发 | 默认会先执行一次,之后依赖变化触发 |
| 能否拿 oldValue | ✔ 可以 | ❌ 不行 |
| 使用场景 | 监听明确数据 | 不明确依赖的副作用逻辑 |
🎯 四、什么时候用 watch?什么时候用 watchEffect?(场景记一下)
✔ watch 适合
- 明确监听某变量:
count、props.id - 需要比较 oldValue / newValue
- 有复杂条件逻辑
- 想严格控制触发
✔ watchEffect 适合
- 自动根据依赖触发,不想手动声明监听项
- 监听多个变量
- 处理副作用:打印日志、请求 API、操作本地存储
例子:监听多个变量:
scss
watchEffect(() => {
console.log(a.value + b.value)
})
🎯 五、面试官常见追问(附高分答案)
追问 1:watchEffect 不会工作或一直触发,是为什么?
答案:因为 watchEffect 会自动追踪所有读取到的响应式变量。
如果在逻辑里访问了不必要的响应式数据,会导致频繁触发。
例子:
scss
watchEffect(() => {
console.log(stateA.value)
console.log(stateB.value) // 明明不想监听,却访问到了
})
追问 2:watchEffect 和 watchEffect(() => {}, { flush: 'post' }) 的区别?
post 能保证 DOM 已更新。
例如:
scss
watchEffect(() => {
console.log(el.value.offsetHeight) // 有可能是旧 DOM
}, { flush: 'post' })
追问 3:可以不用 watchEffect,全部用 watch 吗?
可以,但不推荐。
watchEffect 的优势:
- 写法简单
- 自动收集依赖
- 更适合复杂副作用逻辑
追问 4:watchEffect 会不会比 watch 性能差?
会。
原因:
- 它自动收集依赖
- 每次执行都会重新收集依赖
- 逻辑写不好容易死循环
所以 watchEffect 适合用在 轻量副作用。
追问 5:onInvalidate 是干嘛用的?
比如你发了一个异步请求,依赖变化时需要取消之前的请求。
scss
watchEffect((onInvalidate) => {
const id = count.value
const controller = new AbortController()
fetch(`/api/${id}`, { signal: controller.signal })
onInvalidate(() => controller.abort())
})
这是非常加分的高级用法。