【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())
})

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

相关推荐
小陈工10 分钟前
Python Web开发入门(十一):RESTful API设计原则与最佳实践——让你的API既优雅又好用
开发语言·前端·人工智能·后端·python·安全·restful
星空13 分钟前
前段--A_2--HTML属性标签
前端·html
三万棵雪松28 分钟前
【Linux 物联网网关主控系统-Web部分(一)】
linux·前端·嵌入式linux
摸鱼仙人~44 分钟前
增量快照 vs 结构化共享:适用场景全解析
前端·vue.js
2301_771717211 小时前
Jackson的使用方法详解
java·服务器·前端
A923A1 小时前
【小兔鲜电商前台 | 项目笔记】第八天
前端·vue.js·笔记·项目·小兔鲜
DYuW5gBmH1 小时前
Chrome DevTools MCP 让 AI 无缝接管浏览器调试会话
前端·chrome·chrome devtools
qq12_8115175152 小时前
Java Web 影城会员管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
java·前端·mybatis
独角鲸网络安全实验室2 小时前
Java常见面试题及答案汇总(2025最新版)
java·安全·面试·面试题·考试·考试题·面试攻略
胖咕噜的稞达鸭3 小时前
总结面试经验TCP和UDP的区别,TCP慢启动机制,拥塞控制,Linux指令,DNS的理解,TLS握手流程
tcp/ip·面试·udp