Vue 内存泄漏的解决方案需结合框架特性与常见场景,通常由未释放的引用、全局变量、事件或第三方库引起。以下是系统化的解决思路和扩展知识点:
1. 组件实例未销毁
问题:已卸载的组件仍被外部引用(如全局变量、事件总线)。
解决方法:
- 在 `beforeUnmount` 或 `unmounted` 生命周期中手动清除引用。
- 避免在 `setup()` 外用 `reactive/ref` 包裹全局变量。
javascript
// 错误示例
const globalState = reactive({ data: null });
// 正确做法(组件内使用后用 weakMap 弱引用或主动置空)
2. 事件未移除
问题:组件内注册的全局事件(如 `window.addEventListener`)未解绑。
解决方法:
- 在 `unmounted` 中对称移除:
javascript
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
- 使用 `vue-use` 等工具库的自动化管理。
3. 定时器或异步任务未清理
问题:`setInterval` 或未完成的 `Promise` 持续持有组件引用。
解决方法:
- 定时器需在 `unmounted` 中清除:
javascript
const timer = setInterval(...);
onUnmounted(() => clearInterval(timer));
- 使用 `AbortController` 取消未完成的异步请求。
4. 第三方库资源泄漏
问题:地图、图表库等未正确销毁实例。
解决方法:
- 查阅库文档确认销毁 API(如 ECharts 的 `dispose`)。
- 结合 `onUnmounted` 调用释放方法:
javascript
const chart = echarts.init(dom);
onUnmounted(() => chart.dispose());
5. 闭包陷阱
问题:闭包意外捕获组件变量(如 `setTimeout` 回调)。
解决方法:
- 使用弱引用(`WeakMap/WeakSet`)或拆分逻辑到 Composables。
- 避免在异步回调中直接引用组件 `ref`。
6. Vuex/Pinia 状态残留
问题:未清理的状态订阅或持久化缓存。
解决方法:
- 调用 `unsubscribe` 移除。
- 动态模块需通过 `store.unregisterModule` 卸载。
7. 路由守卫残留
问题:全局路由守卫 `beforeEach` 中保留了组件上下文。
解决方法:
- 在守卫逻辑中避免直接操作组件实例。
- 使用 `route.meta` 传递数据而非闭包。
8. 开发工具检测
工具辅助:
- Chrome DevTools 的 Memory 面板记录堆快照,对比组件卸载前后的内存占用。
- 使用 `performance.memory` API 监控运行时内存变化。
- Vue DevTools 检查游离的组件节点。
扩展知识:
弱引用场景:通过 `WeakMap` 存储外部依赖,无需手动释放。
虚拟滚动优化:长列表采用 `vue-virtual-scroller` 减少 DOM 内存占用。
SSR 内存管理:服务端渲染需注意 `onServerPrefetch` 中的异步请求清理。
内存泄漏的定位需结合具体场景分析,重点排查生命周期钩子、全局状态和异步任务。持续监控内存变化并建立代码审查规范可显著降低泄漏风险。