【vue高频面试题】第 10 题:`watch` VS `watchEffect` 的区别是什么?触发时机有什么不同?

第 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 适合

  • 明确监听某变量:countprops.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())
})

这是非常加分的高级用法。

相关推荐
前端一课32 分钟前
【vue高频面试题】第 11 题:Vue 的 `nextTick` 是什么?为什么需要它?底层原理是什么?
前端·面试
h***346338 分钟前
SpringBoot3.3.0集成Knife4j4.5.0实战
android·前端·后端
Yanni4Night38 分钟前
数据可视化神器Heat.js:让你的数据热起来
前端·javascript
Lazy_zheng38 分钟前
前端页面更新检测实战:一次关于「用户不刷新」的需求拉扯战
前端·vue.js·性能优化
前端一课41 分钟前
【vue高频面试题】第9题:Vue3 的响应式原理是什么?和 Vue2 的响应式有什么区别?为什么 Vue3 改用了 Proxy?
前端·面试
Demon--hx41 分钟前
[C++]迭代器失效问题
前端·c++
GISer_Jing43 分钟前
前端架构学习
前端·学习·架构
前端一课43 分钟前
【vue高频面试题】第4题:Vue 3 中的 setup() 是什么?它的执行时机是什么?能做什么?
前端·面试
前端一课44 分钟前
【vue高频面试题】第5题:Vue3 的父子组件通信方式有哪些?分别适用于什么场景?
前端·面试