跳到主要内容

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());

渲染引擎选择

维度CanvasSVG
数据量大数据量优小数据量优
交互自定义事件DOM 事件
内存固定(位图大小)随元素数线性增长
导出位图矢量(可打印)
无障碍较好
// 切换渲染器
const chart = echarts.init(container, null, { renderer: 'svg' });
选择建议
  • 数据量 > 1000 或动画频繁 → Canvas
  • 需要高清打印/SEO/无障碍 → SVG
  • 移动端/低端设备 → Canvas(性能更好)

核心架构

ECharts 采用 MVC 架构

  • Model 层:解析和管理配置项(ComponentModelSeriesModel
  • View 层:将 Model 数据渲染为图形(bindaryComponentViewbindarySeriesView
  • 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 如何处理大数据量?

答案

  1. sampling: 'lttb':降采样算法,减少渲染点数
  2. large: true:开启大数据优化模式
  3. progressive:渐进式渲染,分帧绘制
  4. dataZoom:只展示可视区域数据
  5. appendData:增量追加数据
  6. 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 参数。

相关链接