目录
[1.watch 的基本用法](#1.watch 的基本用法)
[2.watchEffect 的基本用法](#2.watchEffect 的基本用法)
[4.对于watch 和 watchEffect的注意事项](#4.对于watch 和 watchEffect的注意事项)
[4.1 选择合适的侦听器](#4.1 选择合适的侦听器)
[4.2 避免副作用无限循环](#4.2 避免副作用无限循环)
[4.3 侦听深层对象](#4.3 侦听深层对象)
[4.4 侦听多个数据源](#4.4 侦听多个数据源)
[5.1 watch 适配的工程场景](#5.1 watch 适配的工程场景)
[5.2 watchEffect 适配的应用场景](#5.2 watchEffect 适配的应用场景)
在使用 Vue 3 组合式 API 开发时,你一定遇到过这样的问题:
我想监听某个变量,但不确定该用 watch 还是 watchEffect?
它们看起来一样,却经常用错,导致副作用反复触发、逻辑混乱甚至性能浪费!
本文带你彻底搞明白两者区别,让你以后写监听逻辑不再纠结。
1.watch 的基本用法
**作用:**watch用于侦听一个或者是对个响应式数据源的变化,并在变化时执行回调函数。
javascript
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`count 变化了,从 ${oldVal} 变成了 ${newVal}`);
});
说明:
- 需要明确的指出需要监听的数据源,在上述案例中的数据源就是 count。
- 回调函数需要接收新值和旧值作为参数,也就是变化之前的值和变化之后的值。当监听对值发生改变时会自动传入改变之前的值和改变之后的值。
- watch适合监听特定数据的变化,针对特定数据执行特定的副作用函数。
2.watchEffect 的基本用法
**作用:**watchEffect 会立即执行传入的副作用函数,并自动跟踪其中使用的所有响应式依赖,当依赖变化时重新执行副作用。也就是说相比于watch不需要指定需要侦听的数据源,只要watchEffect回调函数内部的响应式数据发生了改变就会立即执行副作用函数。
javascript
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
console.log(`count 的值是:${count.value}`);
});
说明:
- 在上述示例中我们可以发现watchEffect并没有任何监听的数据源,这是因为watchEffect会自动收集响应式依赖,所以不需要我们去指定数据源。
- 当副作用函数内部的任意响应式数据源发生了改变都会立即的执行副作用函数,这也是区别watch和watchEffect的用法之一,对于副作用函数会有多个值的变化,我们可以使用watchEffect来执行变化之后的副作用函数。对于特定数据可以使用watch来执行特定的回调函数。
- 适合副作用逻辑依赖多个响应式数据,且依赖动态变化的场景。
3.图表总结区别
| 特性 | watch | watchEffect |
|---|---|---|
| 依赖收集 | 手动指定监听的响应式数据源 | 自动追踪副作用函数中使用的响应式数据 |
| 执行时机 | 依赖变化后执行回调 | 立即执行副作用函数,依赖变化时重新执行 |
| 传入参数 | 新值和旧值 | 无参数 |
| 适用场景 | 需要精确监听某个(些)响应式数据 | 依赖复杂或动态的响应式数据 |
| 取消监听 | 返回停止函数,可手动停止监听 | 返回停止函数,可手动停止监听 |
4.对于watch 和 watchEffect的注意事项
4.1 选择合适的侦听器
- 如果你只需要监听单个或少量响应式变量,并且需要对新旧值进行比较,使用
watch更合适。 - 如果副作用逻辑依赖多个响应式变量,且依赖关系复杂或动态变化,使用
watchEffect更方便。
4.2 避免副作用无限循环
在 watchEffect 中,副作用函数内如果修改了被侦听的响应式数据,可能导致无限循环。此时需谨慎设计逻辑,避免触发自身变化
javascript
watchEffect(() => {
count.value++ // 会导致无限触发!
});
**说明:**当触发了这个侦听器之后,由于被真侦听的响应式数据是自增的,所以当函数被执行,响应式数据会发生改变,此时又会触发侦听器的副作用函数,如此循环。
4.3 侦听深层对象
watch 支持深度监听:watchEffect 不需要配置深度监听,因为它自动追踪依赖。
javascript
watch(
() => state.user,
(newVal) => {
console.log('user 对象变化了', newVal);
},
{ deep: true }
);
4.4 侦听多个数据源
watch 支持监听多个响应式数据:
javascript
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(newCount, newName);
});
5.watch和watchEffect的应用场景
5.1 watch 适配的工程场景
关键特征:监听目标明确、变化需要驱动后续行为
watch 更像是事件监听器 :有人变化 → 我才做事
它的使用前提是 你非常清楚要监听谁、监听变化会触发什么行为。
**场景1:**监听登录状态变化 → 初始化用户信息
某个状态发生变化时执行一次或有限次数的任务最适合 watch
javascript
watch(
() => user.isLogin,
(isLogin) => isLogin && initUserProfile()
);
**场景2:**监听路由参数变化 → 刷新当前详情内容
适用于同组件内切换展示不同 ID 数据的情况,避免页面重新挂载。
javascript
watch(
() => route.params.id,
(id, oldId) => id !== oldId && getDetail(id)
);
**场景3:**监听分页页码变化 → 重新请求列表数据
javascript
watch(
() => pagination.page,
() => fetchList(pagination.page)
);
**场景4:**监听深度对象变化 → 用于数据草稿保存/缓存
javascript
watch(
() => profileForm,
(newVal) => saveDraft(newVal),
{ deep: true }
);
应用场景:档案编辑器、简历编辑、客户信息录入 CRM
5.2 watchEffect 适配的应用场景
**核心特征:**依赖关系未知 / 复杂 / 可能演变
watchEffect 适用于**「只关心结果」的应用逻辑**
即:任何可能影响结果的依赖变化,自动重新执行副作用。
无需指名监听谁,不关心源,只关心逻辑输出是否应刷新。
**场景1:**实时搜索联动 → 输入即触发请求
无需写多个 watch,只需把效果表达清楚,系统自动追踪字段。
javascript
watchEffect(() => {
fetchList({ keyword: search.value });
});
**场景2:**筛选条件组合变化 → 自动刷新结果
多条件筛选列表场景是 watchEffect 的强项:多依赖自动管理、不需分别声明监听
javascript
watchEffect(() => {
getOrders({
status: statusFilter.value,
date: dateRange.value,
type: orderType.value,
});
});
**场景3:**自动持久化配置/偏好(不用深度监听)
对象值变化时完全自动被追踪,无需 deep 配置。
javascript
watchEffect(() => {
localStorage.setItem('userSettings', JSON.stringify(settings.value));
});
**场景4:**副作用变化绑定全局行为(如标题、通知 badge)
javascript
watchEffect(() => {
document.title = `未读消息(${messageStore.unreadCount})`;
});
说明:消息提醒、客服系统、未处理任务、审批数量动态显示