数据量才几万条,ECharts 图表渲染就要 3 秒以上,用户拖拽 dataZoom 的时候卡得像幻灯片。反复检查代码,配置项翻来覆去看,觉得“官方文档不就是这么写的吗”。问题不在 ECharts 本身——66.6k GitHub stars、Apache 基金会顶级项目——它扛得住。问题出在理解还停留在“能画出来就行”的阶段。

这篇文章拆解 8 个实战技巧,不讲理论废话,直接上手。

1. Canvas 还是 SVG?选错渲染器性能差10倍

大多数人用 ECharts 的时候根本没想过渲染器的问题——初始化的时候不传 renderer 参数,默认就是 Canvas。但默认不等于最优。

核心区别:Canvas 把整个图表画到一张位图上,适合数据量大、动画多的场景,缺点是放大模糊;SVG 每个图形元素是独立的 DOM 节点,适合数据量小、需要高保真打印或元素级交互的场景,数据量一上来 DOM 节点爆炸。

选型判断标准:

场景

推荐渲染器

原因

数据量 > 1000 个图形元素

Canvas

位图绘制,不创建 DOM 节点

需要 CSS hover / 点击单个元素

SVG

元素是 DOM,可直接绑定事件

大屏 / 实时监控面板

Canvas

高频重绘下帧率更稳

打印 / 导出高清图

SVG

矢量无损缩放

const chart = echarts.init(document.getElementById('main'), null, {
    renderer: 'svg' // 或 'canvas'(默认)
});

踩坑记录:一个仪表盘大屏上 20 多个图表全部用 SVG 渲染,Chrome 开发者工具一看 DOM 节点 12000+,改成 Canvas 之后 FPS 从 15 拉到 58。

2. 渐进式渲染:大数据量的第一道防线

当需要在一张图上渲染上万个数据点时,浏览器主线程会被阻塞——用户看到白屏持续 2-3 秒后图表突然“蹦”出来。ECharts 内置了渐进式渲染,原理很简单:不是一次性把所有数据画完,而是分批画,每批之间让出主线程。

const option = {
    series: [{
        type: 'scatter',
        data: largeData, // 假设 50000 个点
        progressive: 400,   // 每次渲染 400 个图形元素
        progressiveThreshold: 1000 // 数据量超过 1000 时启用渐进式
    }]
};

两个参数的含义:

  • progressive:每批渲染多少个元素。值越小越流畅,但总渲染时间越长,推荐 200-1000
  • progressiveThreshold:数据量低于此值时不启用渐进式,省去分批开销

5 万散点实测:不开启渐进式首次渲染约 2800ms 且严重卡顿;progressive: 400 总时间略长但无明显卡顿。

3. 数据采样:10万点折线图照样丝滑

当折线图或散点图有 10 万+数据点时,Canvas 绘制的点数超出了视觉可分辨范围。人眼无法区分 1 像素内的多个点,那就没必要全画。

ECharts 内置了 sampling 配置:

series: [{
    type: 'line',
    sampling: 'average', // 可选:'average'、'max'、'min'
    data: largeData
}]

也可以结合 large 模式使用:

series: [{
    type: 'scatter',
    large: true,
    sampling: 'average',
    data: largeData
}]

4. 按需引入:打包体积从1MB砍到200KB

ECharts 全量引入体积约 1MB+。如果只用折线图和柱状图,完全没必要把地图、关系图等模块全打包进去。

// 按需引入示例
import * as echarts from 'echarts/core';
import { LineChart, BarChart } from 'echarts/charts';
import { GridComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';

echarts.use([LineChart, BarChart, GridComponent, TooltipComponent, CanvasRenderer]);

5. 增量更新:别再全量 setOption 了

每次调用 setOption 全量更新,ECharts 需要比对整个配置树的差异,数据量大时开销可观-5。如果只是数据变化,只更新数据即可:

// ❌ 全量更新
myChart.setOption(option);

// ✅ 增量更新
myChart.setOption({
    series: [{
        data: newData
    }]
});

对于高频更新场景,用 requestAnimationFrame 批量执行,降低重排与重绘次数。

6. 关闭不必要的动画和交互

不需要动画效果时,把 animation 关闭,减少不必要的性能开销。不需要的交互组件如 tooltip、toolbox、dataZoom、visualMap 也一并关闭-。

const option = {
    animation: false,
    tooltip: { triggerOn: 'none' }, // 或 'click' 减少实时计算
    toolbox: { show: false },
    dataZoom: [] // 不需要时不要配置
};

7. dataZoom + appendData:按需加载

通过 dataZoom 组件让用户缩放查看数据,配合 appendData 实现增量加载:

// 分片加载
const loadDataChunk = (start, end) => {
    const dataChunk = largeData.slice(start, end);
    myChart.appendData({
        seriesIndex: 0,
        data: dataChunk
    });
};

// 先加载第一片
loadDataChunk(0, 1000);

appendData 不会全量重绘,只追加新数据,适合大数据流式加载场景。

8. 内存泄漏:dispose 不调,内存迟早爆

页面切换或组件销毁时,不调用 dispose() 释放 ECharts 实例,内存会持续增长,最终导致浏览器标签页崩溃-。

// 组件销毁时
beforeDestroy() {
    if (this.chart) {
        this.chart.dispose();
        this.chart = null;
    }
}

同时注意事件解绑:

// 监听事件后及时解绑
chart.on('click', handler);
// 销毁前
chart.off('click', handler);

总结

ECharts 性能优化的核心思路是:

  1. 选对渲染器:数据多选 Canvas,交互细选 SVG
  2. 减少渲染量:渐进式渲染 + 数据采样
  3. 减少重绘:增量更新代替全量 setOption
  4. 按需加载:按需引入模块 + dataZoom 分片
  5. 及时释放:dispose 销毁实例,防止内存泄漏

建议先通过 Chrome DevTools Performance 面板定位瓶颈(渲染耗时、主线程长任务),再针对性优化。