Vue3-watchEffect
2025年12月6日 21:39
watchEffect 是 Vue 3 组合式 API 提供的另一个侦听器。它和 watch 目标一致(在数据变化时执行副作用),但使用方式更简洁,因为它会自动追踪依赖。
1.核心理念
watchEffect 的核心理念是: “立即执行一次,并自动追踪函数内部用到的所有响应式数据,在它们变化时重新执行。”
它只需要一个参数:一个函数。
<template>
<p>User ID: {{ userId }}</p>
<button @click="changeUser">切换用户</button>
</template>
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
const userId = ref(1);
// 1. 立即执行:
// watchEffect 会在 <script setup> 执行到这里时立即运行一次
// 它会自动“发现”内部用到了 userId.value
watchEffect(() => {
// 当 watchEffect 运行时,它会追踪所有被“读取”的 .value
console.log(`(watchEffect) 正在获取 ID: ${userId.value} 的数据...`);
// 假设的 API 调用
// fetchUserData(userId.value);
});
// 2. 自动追踪变化:
// 当 userId 变化时,上面的函数会 *自动* 重新运行
const changeUser = () => {
userId.value++;
};
</script>
2.watchEffect 与 watch 的核心区别
| 特性 | watch |
watchEffect |
|---|---|---|
| 依赖源 | 手动指定 (必须明确告诉它侦听谁) | 自动追踪 (它会侦听函数体内部用到的所有数据) |
| 立即执行 | 默认不会 (需配置 { immediate: true }) |
默认立即执行 |
| 访问旧值 |
可以 (回调参数 (newVal, oldVal)) |
不可以 (回调不接收任何参数) |
| 侧重点 | 适合精确控制 | 更“轻量级”,适合简单的、自动化的副作用 |
3.watchEffect 的高级用法
- 停止侦听:
watchEffect同样会返回一个“停止句柄” (Stop Handle) 函数。
在
<script setup>中,它也会自动绑定到组件生命周期,并在组件卸载时自动停止,所以你通常不需要手动停止。
import { watchEffect } from 'vue';
const stopHandle = watchEffect(() => {
// ...
});
// 在未来的某个时刻
stopHandle(); // 停止这个 effect
- 清除副作用 (
onInvalidate):watchEffect的回调函数可以接收一个onInvalidate函数作为参数。这个函数用于注册一个“失效”时的回调。
当
watchEffect即将重新运行时(或在侦听器被停止时),onInvalidate注册的回调会先执行。这在处理异步操作竞态时非常有用(例如,短时间内多次触发 API 请求)。举个栗子
<template>
<div>
<h3>onInvalidate</h3>
<p>在控制台中查看日志,并快速输入:</p>
<input v-model="query" />
<p>当前查询: {{ query }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
const query = ref('vue');
watchEffect((onInvalidate) => {
// 1. 创建 AbortController
const controller = new AbortController();
const { signal } = controller;
console.log(`(Fetch) 正在搜索: ${query.value}`);
// 2. 使用 Open Library 的 API
// 对 URL 进行了编码 (encodeURIComponent) 以处理特殊字符
const url = `https://openlibrary.org/search.json?q=${encodeURIComponent(query.value)}`;
fetch(url, { signal })
.then(response => response.json()) // 将响应解析为 JSON
.then(data => {
// 3. 成功获取数据
console.log(`(Success) 成功获取: ${query.value}`, data.docs.length, '条结果');
})
.catch(err => {
// 4. 捕获错误
if (err.name === 'AbortError') {
// 预期的中止错误
console.log(`(Abort) 已取消上一次请求: ${query.value}`);
} else {
// 其他网络错误
console.error('Fetch 错误:', err);
}
});
// 5. 注册“失效”回调
onInvalidate(() => {
console.log('...查询变化太快,Effect 即将失效,中止上一个请求...');
// 6. 中止上一次的 fetch 请求
controller.abort();
});
});
</script>
当快速输入
333时,控制台打印
![]()
检查网络,前两次的快速请求都被取消了,只有最后一次成功
![]()
4.总结
onInvalidate 适用于任何有“清理”需求的副作用:
-
取消异步请求:(最常用)
fetch,axios等。 -
清除定时器:清除
setTimeout或setInterval。 -
解绑事件监听器:如果在
watchEffect内部用window.addEventListener动态添加了一个事件,你可以在onInvalidate中用window.removeEventListener将其移除,防止内存泄漏。
两个关键区别:
-
防抖/节流 (Debounce/Throttle) (用
watch):- 目标:减少执行次数。
- 逻辑:“等一等再执行”。
-
清理副作用 (Cleanup) (用
watchEffect+onInvalidate):- 目标:防止旧操作干扰新操作。
- 逻辑:“开始新的之前,先确保旧的已经停止了”。