onCleanup必须在watch回调同步路径开头调用以注册清理函数,如AbortController.abort(),再发起异步请求;否则因无活跃副作用而报错。

在 watch 回调中使用 onCleanup 取消过期请求,和 watchEffect 原理一致,但写法更明确:它作为回调函数的第三个参数传入,专为清理上一轮未完成的副作用而设。关键不是“能不能用”,而是“必须在发起异步操作前注册”,否则清理逻辑会失效或报错。
onCleanup 在 watch 中的正确调用位置
onCleanup 必须出现在回调函数的同步执行路径上,且要在任何 await、setTimeout 或网络请求发起之前调用。Vue 需要在这个时机把清理函数绑定到当前副作用实例上。
- ✅ 正确:先创建
AbortController,立刻注册onCleanup(() => controller.abort()),再发请求 - ❌ 错误:先
await fetch(),再调onCleanup—— 此时响应式上下文已结束,会提示 “no active effect found”
取消请求的标准结构(推荐 AbortController)
每次 watch 触发新监听值时,都会新建控制器并绑定信号;旧请求靠 onCleanup 自动中断,避免状态覆盖。
- 新建
const controller = new AbortController() - 在回调开头立即写:
onCleanup(() => controller.abort()) - 把
controller.signal传给fetch或 Axios(1.5+ 支持) - 在
catch中过滤错误:if (err.name !== 'AbortError') { /* 处理真实错误 */ }
防止响应错乱的双重保险
即使请求被 abort,极小概率下响应仍可能进入 then。加一个标记可确保只更新最新一轮的状态:
- 定义
const isLatest = ref(true) - 在
onCleanup中设为false:onCleanup(() => { isLatest.value = false }) - 在请求成功后检查:
if (isLatest.value) data.value = res.data
其他常见清理场景
除请求外,所有长期存在的资源都适用同一模式:
- 定时器:
const timer = setInterval(...); onCleanup(() => clearInterval(timer)) - 事件监听:
window.addEventListener('scroll', handler); onCleanup(() => window.removeEventListener('scroll', handler)) - 第三方实例(如 ECharts):
const chart = echarts.init(el); onCleanup(() => chart.dispose())


















