组件销毁后异步任务需主动清理,通过清理函数清除定时器、监听器等副作用,并用挂载状态标识阻断无效回调,优先使用abortcontroller等可中断机制,最后验证清理是否生效。

组件销毁后,异步任务不会自动终止,它们可能继续运行、尝试更新已卸载的组件状态,或触发 emit、setState 等操作,导致警告、内存泄漏甚至崩溃。关键不是“等它结束”,而是主动切断执行链路。
用清理函数拦截副作用出口
React 的 useEffect 返回清理函数,Vue 的 onBeforeUnmount 或 unmounted 钩子,都是你唯一可控的“关闸时机”:
- 在 useEffect 内启动定时器、轮询或监听器时,必须同步保存其控制句柄(如
timeoutId、intervalId、emitter.on()返回的off函数) - 清理函数中严格调用对应取消方法:
clearTimeout、clearInterval、removeEventListener、off()、controller.abort() - 第三方库(如 ECharts、Monaco Editor)务必调用其明确提供的
dispose()或destroy()方法,不能依赖自动回收
用挂载状态标识阻断无效回调
即使清理了源头,已发出但未响应的 Promise(如 API 请求)仍可能在返回后执行逻辑。此时需在回调入口加一层存活判断:
- React:用
useRef创建布尔标记,在useEffect清理函数中设为false;所有then/catch中先检查isMounted.current === true - Vue 3:导入
isMounted,在emit前加if (!isMounted()) return - Vue 2:手动在
beforeDestroy中设置this.isDestroyed = true,并在所有 emit / setState 前校验
优先使用可中断的原生机制
比起事后拦截,从源头支持取消更可靠:
- 网络请求统一走
AbortController:传入signal到fetch或 Axios(配置cancelToken),清理函数中直接controller.abort() - 避免裸写
setTimeout或setInterval,封装成带 cancel 方法的工具函数 - 轮询类逻辑不要用无限
while (true)+await,改用递归setTimeout并每次检查挂载状态
验证是否真正清理到位
光写清理逻辑不等于生效,要靠可观测手段确认:
- 在清理函数里加
console.log('cleanup triggered'),配合 Vue Devtools 或 React Devtools 查看组件实例是否真实消失 - 在异步回调开头打印
this?.isMounted ?? 'no instance'或isMounted.current,观察销毁后是否还有输出 - 浏览器控制台搜索 “unmounted”、“Can’t perform a React state update” 等关键词,定位残留调用点











