onCleanup在watch回调因依赖变化将重执行或组件卸载时立即执行上一轮清理函数,用于取消过期请求;需同步注册并配合AbortController,且响应后须校验signal.aborted。

在 Vue 3 的 watch 中使用 onCleanup 取消过期请求,核心是:每次监听触发新请求前,自动终止上一次尚未完成的请求,避免响应错乱或状态覆盖。
onCleanup 什么时候被调用?
onCleanup 不是每次回调执行完就运行,而是在以下两种情况之一发生时,**立即执行上一次注册的清理函数**:
- 当前 watch 回调因依赖变化即将重新执行(即旧副作用被覆盖)
- 组件卸载(
unmounted)且该 watch 尚未被新执行覆盖
它清理的是“上一轮未结束的异步操作”,不是本轮逻辑。
正确写法:同步注册 + AbortController
必须在 watch 回调的同步执行阶段调用 onCleanup,且推荐搭配 AbortController —— 这是浏览器原生、标准、可靠的请求取消机制。
立即学习“前端免费学习笔记(深入)”;
✅ 正确示例:
const searchKeyword = ref('')<br>const result = ref(null)<br><br>watch(searchKeyword, (newVal, oldVal, onCleanup) => {<br> if (!newVal.trim()) return<br> <br> const controller = new AbortController()<br> // ✅ 立即注册清理:同步位置,就在 fetch 前<br> onCleanup(() => controller.abort())<br> <br> fetch('/api/search', {<br> signal: controller.signal<br> })<br> .then(res => res.json())<br> .then(data => {<br> // ✅ 安全更新:检查是否已被取消,避免过期响应污染状态<br> if (!controller.signal.aborted) {<br> result.value = data<br> }<br> })<br> .catch(err => {<br> // ✅ 只处理非中止错误<br> if (err.name !== 'AbortError') {<br> console.error('请求失败:', err)<br> }<br> })<br>})
常见错误要避开
-
异步后注册:比如
await fetchData(); onCleanup(...)—— 此时响应式上下文已退出,Vue 报错 “no active effect found” - 用 setTimeout 包裹 onCleanup:延迟调用导致无法绑定到当前副作用实例
- 忽略 aborted 判断:fetch 成功返回后仍直接赋值,可能把旧关键词的结果写进新关键词的状态里
-
混用 axios CancelToken(已废弃):Vue 3 场景下优先用
signal,更轻量、标准、无需额外配置
其他适用场景也遵循同一逻辑
除了 fetch,任何需要手动释放的异步资源都可套用这个模式:
- 用
setTimeout实现搜索防抖 →onCleanup(() => clearTimeout(timer)),timer 建议用闭包局部变量,不依赖 ref - 监听
window.resize→onCleanup(() => window.removeEventListener('resize', handler)) - 初始化 ECharts / Three.js 实例 →
onCleanup(() => chart.dispose?.())


















