Vue + ECharts 内存泄露排查与优化实践

166 阅读3分钟

原文链接

在开发中后台或者数据可视化页面时,我们经常会遇到 内存泄露 的问题,尤其是使用 Vue 和 ECharts 时。本文将结合实际项目经验,详细讲解如何排查引用导致的内存泄露,并提供实用的优化方案。

一、现象分析

在我们的项目中,有一个页面需要循环展示一百个echarts图表,每次点击刷新或者翻页后:

  • 页面依旧能够正常渲染图表;
  • 但是使用 Chrome DevTools 的 Heap Snapshot 工具发现:
    • 堆内存持续增长;
    • Snapshot 体积越来越大。

这说明页面存在 内存泄露

1️⃣ 准备工作:捕获快照

  1. 打开 Chrome DevTools → Memory

  2. 点击可疑的操作,比如下图是我重复点击四次刷新后的效果

点击新一轮刷新后上一轮的线条居高不下,说明内存泄露

640.webp 3. 清空快照并刷新页面,按以下操作获取初始状态的快照图片4. 重复可疑操作(我这里是点击刷新按钮更新图表数据),生成第二个快照5. 最终你会有两个快照:前和后

2️⃣ 对比快照:找新增对象

  1. 选择 第二个快照

  2. 下拉选择右侧的 Comparison(比较)  面板:

  • 选择要对比的快照,默认会与上一个对比
  • 这里会显示所有新增对象的 数量 和 类型
  • 输入datached过滤(datached是分离的意思,也就是脱离了文档流的元素,但是没有被释放)

    图片

  • 我们看No. delta,+表示新增的元素,然后随便选择一个再观察Retainers(Retainer是保留的意思),我们可以看到发生在echarts库,于是大胆猜测刷新过后echart实例没释放。

    图片

  • 然后在回到自己的代码刷新按钮点击后的逻辑处加上销毁echart实例的逻辑

    图片

  • 然后我们再按上面的方法对比快照,发现Detached已经没了图片

        


二、分析内存泄露原因

1. ECharts 实例累积

renderChart 函数每次都会:

var myChart = echarts.init(document.getElementById(chart.id));
myChart.setOption(option);
  • 没有销毁旧实例,导致每次渲染产生新的 ECharts 对象;
  • DOM 节点未解绑旧实例 → Detached DOM。

2. 事件监听器闭包

  • 事件闭包会引用 chart 数据;
  • 累积点击按钮 → 旧数据依然存在 → 内存不断增长。
  • 可能触发旧 DOM 替换 → Detached DOM → 脱离文档流却存在内存中。

三、解决方案

1. 在渲染前销毁旧 ECharts 实例

renderChart(chart) {    
    // 如果已有实例,先销毁    
    if (chart._echartsInstance) {        
        chart._echartsInstance.dispose();    
    }    
    const el = document.getElementById(chart.id);   
    const myChart = echarts.init(el);    
    chart._echartsInstance = myChart;    
    myChart.setOption(option);
}

核心:每个 chart 只保留一个实例,旧实例先 dispose()

2. 手动清理图表实例

this.chartsData.forEach(chart => {  
    if (chart._echartsInstance) chart._echartsInstance.dispose();
});
this.chartsData = [];
  • 在更新 chartsData 前清理旧实例,释放内存。

四、排查技巧总结

  1. 观察 Heap Snapshot 的体积变化
    • 一次增长并不意味着一定是内存泄露,有可能是刷新后做了正常的数据缓存,多刷新几次,每次都打快照,如果持续增长,说明内存可能泄露;
    • 沿 Retainers 找真正的业务引用。
  2. 对比操作前后快照
    • 找新增对象和引用链;
    • 可以判断是 DOM、闭包、事件还是第三方库缓存导致。
  3. 关注 ECharts、事件和 DOM 的生命周期
    • 每次重新渲染前销毁实例;
    • 避免重复注册事件;
    • Vue 组件卸载时清理实例。

五、效果对比

优化前优化后
点击按钮一次,快照增加几十 MB内存稳定增长几乎消失
Detached DOM 数量累积Detached DOM 减少,system持有对象不再增长
Heap Snapshot 体积快速膨胀Heap Snapshot 体积稳定

六、结语

内存泄露不仅影响性能,还可能导致应用崩溃。
在 Vue + ECharts 的项目中,实例管理、事件解绑和 DOM 生命周期清理是关键点。