onunmounted未触发主因是组件未真正卸载,如被keepalive缓存;需先验证卸载(console.log或devtools堆快照),再在钩子中成对清理定时器、事件监听、第三方库实例及abortcontroller等外部资源。

Vue 3 单文件组件(SFC)中实现卸载时的内存泄漏排查与清理,核心不是“等泄漏发生再补救”,而是从设计阶段就建立资源生命周期与组件生命周期的严格对齐。关键在于两点:一是确保 onUnmounted(或 onBeforeUnmount)真正被触发;二是在钩子里精准、成对地释放所有外部引用。
确认组件确实被卸载了
很多“泄漏”其实源于组件根本没销毁——比如被 <keepalive></keepalive> 缓存,此时走的是 deactivated/activated,onUnmounted 压根不会执行。要验证是否卸载成功:
- 在
onUnmounted里加console.log('unmounted'),切换路由或隐藏组件后看控制台是否输出 - 用 Chrome DevTools → Memory 面板拍两次堆快照(打开组件 → 交互 → 关闭组件),对比筛选你的组件名,若实例数量持续增长,说明未卸载或残留引用
- 检查是否用了
:key且 key 没变、是否包裹在<keepalive></keepalive>中、路由是否复用同一组件(如仅参数变化)
定时器与异步任务必须配对清除
setInterval、setTimeout、requestAnimationFrame、fetch 请求等,只要回调里访问了组件内的 ref 或 reactive,就会形成强引用链,阻止 GC 回收。
- 用
ref(null)存储定时器 ID,在onMounted启动,在onUnmounted清除:clearInterval(timer.value) - 发起请求时搭配
AbortController:const controller = new AbortController(),fetch 传入{ signal: controller.signal },在onUnmounted调用controller.abort() - 避免在
.then中直接赋值响应式数据;可先判断状态,但更推荐用 abort 机制主动切断请求链
事件监听器要“加多少,删多少”
全局对象(window、document)、自定义事件总线、第三方库绑定的 DOM 事件,都不会随组件自动解绑。
- 监听函数不能是匿名函数或箭头函数,否则
removeEventListener无法匹配——写成 setup 内的具名函数,或用useRef缓存稳定引用 - 手动绑定后,必须在
onUnmounted中显式调用removeEventListener - 推荐封装为
useEventListener组合式函数,绑定与解绑逻辑天然耦合,不易遗漏
第三方库实例需显式销毁
图表(ECharts、Recharts)、地图(Leaflet、MapLibre)、编辑器(Monaco)、滚动库(Lenis)等,内部持有 DOM、Canvas、WebGL 或长连接,v-if 移除 DOM 并不等于释放资源。
- 查阅文档确认是否有
destroy()、dispose()、close()等方法 - 在
onUnmounted中调用,且注意顺序:先断开数据流(如取消轮询、关闭 EventSource),再调用销毁方法 - 动态挂载的弹层、浮层等 DOM 元素,卸载前应手动
el.remove(),避免第三方库仍持有引用
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










