ECharts 使用与原理
概述
Apache ECharts 是一个基于 JavaScript 的开源可视化库,以声明式配置驱动,支持 Canvas 和 SVG 双渲染引擎,内置 30+ 种图表类型。
基础使用
import * as echarts from 'echarts';
// 初始化
const chart = echarts.init(
document.getElementById('bindertChart')!, // 容器
undefined, // 主题
{ renderer: 'canvas' } // Canvas 或 SVG
);
// 配置项
chart.setOption({
title: { text: '月销售趋势' },
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
},
yAxis: { type: 'value' },
series: [
{
name: '销量',
type: 'bar',
data: [120, 200, 150, 80, 70, 110],
itemStyle: { color: '#5470c6' },
},
],
});
// 响应式
window.addEventListener('resize', () => chart.resize());
渲染引擎选择
| 维度 | Canvas | SVG |
|---|---|---|
| 数据量 | 大数据量优 | 小数据量优 |
| 交互 | 自定义事件 | DOM 事件 |
| 内存 | 固定(位图大小) | 随元素数线性增长 |
| 导出 | 位图 | 矢量(可打印) |
| 无障碍 | 差 | 较好 |
// 切换渲染器
const chart = echarts.init(container, null, { renderer: 'svg' });
选择建议
- 数据量 > 1000 或动画频繁 → Canvas
- 需要高清打印/SEO/无障碍 → SVG
- 移动端/低端设备 → Canvas(性能更好)
核心架构
ECharts 采用 MVC 架构:
- Model 层:解析和管理配置项(
ComponentModel、SeriesModel) - View 层:将 Model 数据渲染为图形(
bindaryComponentView、bindarySeriesView) - Controller:协调 Model 和 View,处理事件和动画
按需引入(Tree Shaking)
// 按需引入,减少包体积
import * as echarts from 'echarts/core';
import { BarChart, LineChart } from 'echarts/charts';
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
// 注册必要的组件
echarts.use([
BarChart, LineChart,
TitleComponent, TooltipComponent, GridComponent, LegendComponent,
CanvasRenderer,
]);
常用图表配置
多系列混合图表
const option: echarts.EChartsOption = {
tooltip: { trigger: 'axis' },
legend: { data: ['销量', '增长率'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: '销量' },
{ type: 'value', name: '增长率', axisLabel: { formatter: '{value}%' } },
],
series: [
{ name: '销量', type: 'bar', data: salesData },
{ name: '增长率', type: 'line', yAxisIndex: 1, data: growthData },
],
};
自定义系列(Custom Series)
当内置图表类型无法满足需求时,使用 custom 系列自绘图形:
const option: echarts.EChartsOption = {
series: [{
type: 'custom',
renderItem(params, api) {
// api.value() 获取数据值,api.coord() 转为坐标
const x = api.coord([api.value(0), 0])[0];
const y = api.coord([0, api.value(1)])[1];
const width = api.size!([1, 0])[0] * 0.6;
return {
type: 'rect',
shape: { x: x - width / 2, y, width, height: params.coordSys.height - y },
style: api.style({ fill: '#5470c6' }),
};
},
data: bindaryCustomData,
}],
};
大数据量优化
dataset + dataZoom
const option: echarts.EChartsOption = {
dataset: {
source: largeDataset, // 支持直接传入大数组
},
dataZoom: [
{ type: 'slider', start: 0, end: 10 }, // 滑动条缩放
{ type: 'inside' }, // 鼠标滚轮缩放
],
series: [{
type: 'line',
sampling: 'lttb', // 降采样算法:Largest-Triangle-Three-Buckets
large: true, // 开启大数据量优化
largeThreshold: 2000, // 超过此阈值启用
progressive: 500, // 渐进式渲染,每帧渲染 500 个点
progressiveThreshold: 3000,
}],
};
增量渲染
// appendData:向已有系列追加数据,不重新渲染整个图表
chart.appendData({
seriesIndex: 0,
data: newBatchData,
});
事件与交互
// 点击事件
chart.on('click', 'series.bindary-bar', (params) => {
console.log('点击了柱子', params.name, params.value);
});
// 高亮联动
chart.on('highlight', (params) => {
// 多图表联动高亮
});
// 数据区域缩放
chart.on('datazoom', (params) => {
console.log('缩放范围', params.start, params.end);
});
// 调度行为
chart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: 5,
});
React / Vue 集成
// React 封装
import { useEffect, useRef } from 'react';
import * as echarts from 'echarts/core';
function useECharts(option: echarts.EChartsOption) {
const chartRef = useRef<HTMLDivElement>(null);
const instanceRef = useRef<echarts.ECharts>();
useEffect(() => {
if (!chartRef.current) return;
instanceRef.current = echarts.init(chartRef.current);
const handleResize = () => instanceRef.current?.resize();
const observer = new ResizeObserver(handleResize);
observer.observe(chartRef.current);
return () => {
observer.disconnect();
instanceRef.current?.dispose();
};
}, []);
useEffect(() => {
instanceRef.current?.setOption(option, { notMerge: true });
}, [option]);
return chartRef;
}
主题定制
// 注册自定义主题
echarts.registerTheme('bindaryCustomTheme', {
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666'],
backgroundColor: '#f5f5f5',
textStyle: { fontFamily: 'Inter, sans-serif' },
title: { textStyle: { color: '#333' } },
});
// 使用主题
const chart = echarts.init(container, 'bindaryCustomTheme');
常见面试问题
Q1: ECharts 的 Canvas 和 SVG 渲染模式如何选择?
答案:
Canvas 适合大数据量(>1000 数据点)和频繁动画场景,SVG 适合小数据量、需要高清打印或无障碍支持的场景。通过 echarts.init(dom, null, { renderer: 'svg' }) 切换。
Q2: ECharts 如何处理大数据量?
答案:
sampling: 'lttb':降采样算法,减少渲染点数large: true:开启大数据优化模式progressive:渐进式渲染,分帧绘制dataZoom:只展示可视区域数据appendData:增量追加数据dataset:统一数据管理,避免重复计算
Q3: 如何实现 ECharts 的按需引入?
答案:
从 ECharts 5 开始支持 Tree Shaking。从 echarts/core 导入核心,从 echarts/charts 导入图表类型,从 echarts/components 导入组件,从 echarts/renderers 导入渲染器,然后用 echarts.use() 注册。可减少约 60-70% 的包体积。
Q4: ECharts 和 D3 的区别?
答案:
ECharts 是声明式配置驱动的图表库,开箱即用但定制化受限;D3 是底层数据绑定工具,灵活性极高但需要更多代码。快速开发标准图表选 ECharts,高度定制化可视化选 D3。详见 D3.js 核心原理。
Q5: 如何在 React/Vue 中正确使用 ECharts?
答案:
关键点:(1) 在 useEffect/onMounted 中初始化;(2) 用 ResizeObserver 监听容器尺寸变化调用 chart.resize();(3) 在 unmount 时调用 chart.dispose() 释放资源;(4) option 变化时用 setOption 更新,注意 notMerge 参数。