前端内存泄漏是常见但容易被忽视的问题,可能导致页面卡顿、崩溃或性能下降。以下是几种典型场景及解决方案:
1. 未清理的全局变量
场景:
- 意外创建全局变量(未使用
var
/let
/const
)。 - 主动挂载到
window
的大对象未释放。
function leak() {
leakedData = new Array(1e6).fill('*'); // 意外全局变量
window.cache = {}; // 主动挂载全局
}
解决方案:
- 使用严格模式
'use strict'
避免意外全局变量。 - 手动释放全局对象:
window.cache = null;
2. 未清除的定时器/回调
场景:
setInterval
/setTimeout
未及时清除。- 回调函数引用了外部变量,导致关联对象无法释放。
const timer = setInterval(() => {
// 频繁操作 DOM 或外部变量
}, 1000);
// 未调用 clearInterval(timer)
解决方案:
- 组件卸载/离开时清理定时器:
clearInterval(timer); clearTimeout(timer);
- 使用
requestAnimationFrame
替代高频定时器。
3. DOM 引用未释放
场景:
- JS 中保留对已移除 DOM 的引用。
const elements = [];
function addElement() {
const element = document.createElement('div');
document.body.appendChild(element);
elements.push(element); // 保存引用
}
// 移除 DOM 但未清理数组引用
document.body.removeChild(element);
解决方案:
- 移除 DOM 后手动释放引用:
elements = [];
- 使用
WeakMap
/WeakSet
存储弱引用(自动回收)。
4. 未解绑的事件监听器
场景:
- 元素移除后未解绑事件监听器。
- 匿名函数导致无法正确解绑。
function addListener() {
const element = document.getElementById('button');
element.addEventListener('click', () => {
console.log('Clicked!');
});
}
// 移除元素但未解绑监听器
解决方案:
- 使用具名函数并解绑:
function handleClick() { /* ... */ } element.addEventListener('click', handleClick); element.removeEventListener('click', handleClick);
- 使用 事件委托 减少监听器数量。
5. 闭包导致的内存泄漏
场景:
- 闭包中持有外部大对象,或闭包被全局变量引用。
function createClosure() {
const largeData = new Array(1e6).fill('*');
return function() {
// 闭包隐式持有 largeData
};
}
const globalClosure = createClosure(); // 全局变量持有闭包
解决方案:
- 及时释放闭包引用:
globalClosure = null;
- 避免在闭包中保留不必要的数据。
6. WebSocket 或第三方库未关闭
场景:
- WebSocket 连接未关闭。
- 第三方库(如地图、图表)未调用销毁方法。
const socket = new WebSocket('ws://example.com');
// 未调用 socket.close();
解决方案:
- 在页面卸载时关闭连接或调用库的
destroy()
方法。
7. 框架特定问题(如 React/Vue)
场景:
- React 组件卸载后未清理
setState
或副作用(如useEffect
)。 - Vue 组件未解绑自定义事件。
解决方案(React):
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer); // 清理函数
}, []);
解决方案(Vue):
beforeUnmount() {
this.$eventBus.off('event', this.handler);
}
检测与调试工具
- Chrome DevTools:
- Memory 面板:通过 Heap Snapshots 对比内存变化。
- Performance 面板:记录内存分配趋势。
- WeakMap/WeakSet:利用弱引用自动释放内存。
- LeakCanary(前端移植版):自动化检测内存泄漏。
总结
- 核心原则:及时清理不再使用的引用(定时器、DOM、事件、闭包)。
- 框架规范:遵循 React/Vue 等框架的生命周期清理逻辑。
- 防患未然:使用严格模式、弱引用、工具检测。
通过合理设计代码结构和及时清理资源,可有效避免大多数内存泄漏问题。