onCleanup 是 watchEffect 的清理函数,用于在副作用被覆盖或组件卸载前取消异步操作;它在依赖变化导致重新执行前或组件卸载时触发,清理上一轮未完成的副作用。

在 Vue 3 的响应式系统中,watchEffect 提供了一种更简洁的“自动追踪依赖 + 立即执行”监听方式。它不像 watch 那样需要显式指定源,而是通过运行时自动收集所访问的响应式数据作为依赖。而它的清除机制 —— onCleanup 回调函数 —— 是处理异步副作用(如请求、定时器、事件监听)的关键,能避免内存泄漏和状态错乱。
onCleanup 是什么?什么时候被调用?
onCleanup 是 watchEffect 的第二个参数(可选)中提供的一个函数,用于注册“清理逻辑”。它会在以下时机被自动调用:
- 当前
watchEffect因响应式依赖变化而重新执行前(即旧副作用即将被丢弃时) - 组件卸载(unmounted)时,如果该副作用尚未被新执行覆盖
⚠️ 注意:onCleanup 不是每次 effect 执行完都调用,只在“上一次 effect 被中断/覆盖/组件销毁”时才触发清理 —— 它清理的是“上一轮未完成的副作用”。
实战:用 onCleanup 取消未完成的 API 请求
搜索框输入关键词触发请求时,用户快速连打,旧请求应被取消,否则可能造成响应覆盖或 UI 错乱:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
立即学习“前端免费学习笔记(深入)”;
import { watchEffect } from 'vue'
import { axios } from '@/utils/http'
watchEffect((onCleanup) => {
const controller = new AbortController()
const fetchData = async () => {
try {
const res = await axios.get('/api/search', {
params: { q: searchQuery.value },
signal: controller.signal // 绑定取消信号
})
results.value = res.data
} catch (e) {
if (e.name !== 'AbortError') {
console.error('请求失败:', e)
}
}
}
fetchData()
// 注册清理:若 effect 重跑或组件卸载,主动 abort 当前请求
onCleanup(() => {
controller.abort()
})
})
实战:用 onCleanup 清理定时器或事件监听器
比如监听鼠标移动位置,但只在某个条件为真时生效,且需确保切换时旧监听被移除:
watchEffect((onCleanup) => {
if (isTrackingMouse.value) {
const handleMove = (e: MouseEvent) => {
mouseX.value = e.clientX
mouseY.value = e.clientY
}
window.addEventListener('mousemove', handleMove)
// 清理:下次 effect 重跑前移除旧监听
onCleanup(() => {
window.removeEventListener('mousemove', handleMove)
})
}
})
✅ 这样即使 isTrackingMouse 频繁切换为 true/false,也不会重复绑定或漏删监听器。
常见误区与注意事项
- 不能在 onCleanup 里访问当前 effect 的响应式变量最新值:它运行在“上一轮闭包环境”,拿到的是上次执行时的快照。如需最新值,请在 effect 主体中读取并缓存
-
onCleanup 必须在 effect 同步执行阶段注册:不能放在异步回调(如
setTimeout或.then())里调用,否则无效 - 多次调用 onCleanup 会覆盖前一次:每次 effect 执行时,只会保留最后一次注册的清理函数
- 不适用于同步副作用清理:比如直接修改 DOM 属性,一般无需 onCleanup;它专为“可能持续存在、需显式终止”的异步/外部资源设计
掌握 watchEffect 和 onCleanup 的配合,能让响应式副作用更健壮、更可控,尤其在表单联动、实时搜索、动画控制等场景中非常实用。

















