Vue组件卸载时未清理定时器、事件监听、第三方库实例等外部资源,是内存泄漏最常见源头;需在onUnmounted中配对清除setInterval/clearInterval、addEventListener/removeEventListener,并调用第三方库的destroy/close方法。

Vue 组件卸载时不做资源清理,是内存泄漏最常见、最容易被忽视的源头。它不总表现为立即崩溃,而是随路由切换、反复显隐逐渐堆积——页面变卡、响应延迟、DevTools 里 JS 堆内存只升不降。真正的问题不在 Vue 自身,而在于我们调用的定时器、事件监听、第三方库实例等“外部资源”没被主动断开。
定时器和异步操作必须配对清理
setInterval、setTimeout、requestAnimationFrame 这类定时任务一旦启动,就会持续持有闭包中的变量引用。组件卸载后若不清除,这些引用无法被垃圾回收。
- 在 onMounted 中创建定时器,同时在 onUnmounted 中调用对应清除函数(clearInterval/clearTimeout/cancelAnimationFrame)
- 避免把定时器 ID 存在全局或父级作用域;推荐用 ref 或局部变量捕获,确保清理逻辑能准确访问到目标实例
- 使用 watchEffect 替代手动 watch + 定时器轮询:它自动追踪依赖、自动停止,无需手动清理
事件监听器要“加多少,删多少”
addEventListener 添加的监听器不会随组件销毁自动消失,尤其 window、document、body 等全局对象上的监听,极易造成泄漏。
- 每次 addEventListener 都应有对应的 removeEventListener,且传入的回调函数必须是同一个引用(不能用匿名函数)
- 可封装一个 useEventListener 组合式函数,在 onUnmounted 内统一解绑,提升复用性和可靠性
- 对于 Vue 内部事件(如 $on/$emit),同样需在卸载前调用 $off 显式注销
第三方库实例必须显式销毁
像 Choices.js、AntV 图表(G2/G6)、MapLibre、WebSocket 实例等,往往内部持有 DOM 节点、Canvas、WebGL 上下文或长连接。仅靠 v-if 移除 DOM 并不能释放它们。
立即学习“前端免费学习笔记(深入)”;
- 查阅该库文档,确认是否存在 destroy()、close()、dispose() 等销毁方法,并在 onUnmounted 中调用
- 注意销毁顺序:先断开事件/数据流(如取消请求、关闭 EventSource),再调用销毁方法
- 对动态挂载的 DOM 元素(如弹层、浮层),应在卸载前手动 remove(),避免残留节点继续被第三方库引用
响应式副作用也需关注边界
watch、watchEffect、computed 等虽由 Vue 自动管理,但在某些边界场景仍可能引发泄漏——比如 watch 的回调中又创建了未清理的定时器,或 watchEffect 内部发起未取消的请求。
- watchEffect 是首选:它自动停止,且能通过 onInvalidate 处理“中断前”的清理(例如取消上一次未完成的 fetch)
- watch 若配合异步操作,务必在回调中检查组件是否已卸载(可用 isRef + unref 判断,或借助 onBeforeUnmount 提前标记)
- 避免在 setup 中直接 new Promise 并长期持有 resolve/reject 引用;如有必要,应在 onUnmounted 中 reject 掉待处理的 promise


















