组件卸载时必须主动清理资源以防止内存泄漏,包括取消定时器和动画帧、显式调用第三方库的destroy方法、成对移除事件监听器、清空DOM引用与闭包变量。

组件卸载时没清理资源,内存泄漏就藏在看不见的地方。它不报错,但会让页面越来越卡、响应变慢,甚至最终崩溃——尤其在频繁切换路由或反复打开弹窗的场景里。
定时器和动画帧必须主动取消
setInterval、setTimeout、requestAnimationFrame 这些看似“自动结束”的操作,一旦创建就会长期驻留,只要回调里引用了组件状态或 DOM 元素,整个实例就无法被回收。
- Vue 中用 onUnmounted 清理:在 setup 里声明 timer = ref(null),启动时赋值,卸载时 clearTimeout(timer.value) 或 clearInterval(timer.value)
- React 中靠 useEffect 清除函数:返回的函数会在组件卸载时执行,直接调用 cancelAnimationFrame 或 clearInterval
- 像 Lenis、Recharts 这类库自带 destroy 方法,务必在卸载时显式调用,不能只依赖框架自动销毁
事件监听器要成对移除,且引用必须一致
addEventListener 和 removeEventListener 不是“名字对上就行”,而是要求传入的函数对象完全相同。用箭头函数或每次渲染都新建的函数,remove 就永远失效。
- Vue 推荐把 handler 写成 setup 中的具名函数,或用 useRef 缓存稳定引用
- React 可用 useCallback 包裹 handler,确保依赖数组稳定
- 监听 window、document 等全局对象时更需谨慎——这些监听不会随组件消失而自动解除,必须手动清理
第三方库实例要主动销毁
图表(如 Recharts)、滚动库(如 Lenis)、弹窗管理器、WebSocket 连接等,往往内部持有大量状态和监听器。它们不会因为你 unmount 组件就自动释放资源。
- 查阅文档确认是否有 destroy / dispose / clear 方法,比如 Lenis.destroy()、Recharts 的 animationManager.destroy()
- 封装自定义 Composable 时,在 onUnmounted 或 useEffect 清理函数中统一调用销毁逻辑
- 避免在 setup 或 useEffect 外部直接 new 实例,防止作用域失控导致无法访问销毁入口
DOM 引用和闭包变量要小心处理
ref 持有 DOM 元素、computed 或 watch 依赖大型数据、闭包捕获了 this 或响应式对象,都可能形成强引用链,阻碍 GC。
- 卸载前清空 ref.value,尤其是 el ref 或第三方插件挂载点
- watch 和 computed 如果依赖外部大对象,考虑用 onInvalidate(Vue)或提前 abort(React)中断订阅
- 避免把数据挂到 window 或全局对象上,这类引用永远不会被回收


















