组件模型详解
ECharts 的组件化架构深度解析:从坐标系到交互组件
📖 概述
ECharts 采用组件化架构,将整个图表系统拆分为多个独立、可复用的组件。每个组件负责特定的功能,通过配置项进行组合和协作。
核心组件分类:
- 📐 坐标系组件: grid, polar, geo, singleAxis
- 📏 轴线组件: xAxis, yAxis, radiusAxis, angleAxis
- 📊 系列组件: series(图表核心)
- 🖱️ 交互组件: tooltip, legend, dataZoom, visualMap
- 🎨 辅助组件: title, graphic, aria
理解组件模型是掌握 ECharts 高级用法的关键。
🔍 核心概念
1. 组件分类体系
2. 组件之间的关系
关系图
坐标系组件 (grid)
↓ 包含
轴线组件 (xAxis, yAxis)
↓ 绑定
系列组件 (series)
↓ 监听
交互组件 (tooltip, legend)
↓ 影响
辅助组件 (title, graphic)1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
实际示例
javascript
option = {
// 1. 定义坐标系
grid: {
left: '10%',
right: '10%',
top: '15%',
bottom: '10%'
},
// 2. 定义坐标轴(绑定到 grid)
xAxis: {
type: 'category',
gridIndex: 0, // 使用第一个 grid
data: ['A', 'B', 'C']
},
yAxis: {
type: 'value',
gridIndex: 0 // 使用第一个 grid
},
// 3. 定义数据系列(绑定到坐标轴)
series: [{
type: 'bar',
xAxisIndex: 0, // 使用第一个 xAxis
yAxisIndex: 0, // 使用第一个 yAxis
data: [10, 20, 30]
}],
// 4. 添加交互组件(监听系列)
tooltip: {
trigger: 'axis' // 监听坐标轴
},
legend: {
data: ['系列1'] // 监听系列名称
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
3. 组件索引机制
ECharts 支持定义多个同类型组件,通过索引引用:
javascript
option = {
// 定义两个独立的网格
grid: [
{ id: 'grid1', left: '5%', right: '55%', top: '10%', bottom: '10%' },
{ id: 'grid2', left: '55%', right: '5%', top: '10%', bottom: '10%' }
],
// 定义四个坐标轴
xAxis: [
{ gridIndex: 0, type: 'category', data: categories1 }, // 使用 grid1
{ gridIndex: 1, type: 'category', data: categories2 } // 使用 grid2
],
yAxis: [
{ gridIndex: 0, type: 'value' }, // 使用 grid1
{ gridIndex: 1, type: 'value' } // 使用 grid2
],
// 定义两个系列,分别使用不同的坐标系
series: [
{
name: '系列1',
type: 'bar',
xAxisIndex: 0, // 使用第一个 xAxis(对应 grid1)
yAxisIndex: 0, // 使用第一个 yAxis(对应 grid1)
data: data1
},
{
name: '系列2',
type: 'line',
xAxisIndex: 1, // 使用第二个 xAxis(对应 grid2)
yAxisIndex: 1, // 使用第二个 yAxis(对应 grid2)
data: data2
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
应用场景:
- 对比图表(左右并排)
- 多Y轴图表
- 混合坐标系图表
💡 核心组件详解
1. 坐标系组件
Grid - 直角坐标系
javascript
grid: {
// 位置控制
left: '10%', // 左边距
right: '10%', // 右边距
top: '15%', // 上边距
bottom: '10%', // 下边距
// 尺寸控制
width: 'auto', // 宽度(默认自动计算)
height: 'auto', // 高度(默认自动计算)
// 其他配置
containLabel: true, // 是否包含坐标轴标签
backgroundColor: 'rgba(0,0,0,0.05)', // 背景色
borderColor: '#ccc', // 边框颜色
borderWidth: 1 // 边框宽度
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
实际应用:
javascript
// 场景1: 留出空间给标题和图例
grid: {
left: '3%',
right: '4%',
top: 80, // 为标题留80px
bottom: '3%',
containLabel: true
}
// 场景2: 固定绘图区域
grid: {
left: 100, // 固定100px
right: 50,
top: 50,
bottom: 100,
containLabel: false
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Polar - 极坐标系
javascript
polar: {
center: ['50%', '50%'], // 中心点位置
radius: '75%', // 半径
// 角度范围
startAngle: 90, // 起始角度
endAngle: 450 // 结束角度
},
// 配合极坐标的轴线
radiusAxis: {
type: 'value',
min: 0,
max: 100
},
angleAxis: {
type: 'category',
data: ['A', 'B', 'C', 'D']
},
// 极坐标系列
series: [{
type: 'bar',
coordinateSystem: 'polar', // 使用极坐标系
data: [10, 20, 30, 40]
}]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
应用场景: 雷达图、玫瑰图、极坐标柱状图
Geo - 地理坐标系
javascript
geo: {
map: 'china', // 地图名称
roam: true, // 允许缩放平移
// 视图范围
center: [104.114129, 37.550339], // 中心经纬度
zoom: 1.2, // 缩放级别
// 样式
itemStyle: {
areaColor: '#eee',
borderColor: '#999'
},
emphasis: {
itemStyle: {
areaColor: '#f3f3f3'
}
}
},
// 地理坐标系列
series: [{
type: 'scatter',
coordinateSystem: 'geo', // 使用地理坐标系
data: [
{ name: '北京', value: [116.407526, 39.90403, 100] },
{ name: '上海', value: [121.473701, 31.230416, 150] }
]
}]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
注意: 使用前必须注册地图
javascript
echarts.registerMap('china', chinaGeoJSON);1
2. 轴线组件
X/Y 轴通用配置
javascript
xAxis: {
// 轴类型
type: 'category', // 'category' | 'value' | 'time' | 'log'
// 数据(category 类型必需)
data: ['A', 'B', 'C'],
// 轴名称
name: '月份',
nameLocation: 'end', // 'start' | 'middle' | 'end'
nameTextStyle: { fontSize: 14 },
// 刻度标签
axisLabel: {
show: true,
rotate: 45, // 旋转角度
formatter: '{value}月', // 格式化
interval: 0 // 显示间隔(0=全部显示)
},
// 轴线
axisLine: {
show: true,
lineStyle: { color: '#333', width: 1 }
},
// 刻度线
axisTick: {
show: true,
alignWithLabel: true // 对齐标签
},
// 分割线
splitLine: {
show: true,
lineStyle: { type: 'dashed', color: '#eee' }
},
// 分割区域
splitArea: {
show: false,
areaStyle: { color: ['#f8f8f8', '#fff'] }
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
3. 系列组件(Series)
系列是 ECharts 的核心,决定图表类型和展示方式。
通用配置
javascript
series: [{
// 基础属性
name: '系列名称',
type: 'bar', // 图表类型
data: [], // 数据
// 坐标系绑定
xAxisIndex: 0,
yAxisIndex: 0,
coordinateSystem: 'cartesian2d', // 'cartesian2d' | 'polar' | 'geo'
// 样式配置
itemStyle: {}, // 图形样式
lineStyle: {}, // 线条样式
areaStyle: {}, // 区域填充样式
label: {}, // 标签
// 交互状态
emphasis: {}, // 高亮状态
blur: {}, // 模糊状态
select: {}, // 选中状态
// 动画配置
animation: true,
animationDuration: 1000,
animationEasing: 'cubicOut',
animationDelay: 0
}]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
4. 交互组件
Tooltip - 提示框
javascript
tooltip: {
// 触发方式
trigger: 'item', // 'item' | 'axis' | 'none'
// 触发条件
triggerOn: 'mousemove', // 'mousemove' | 'click' | 'none'
// 显示控制
show: true,
showDelay: 0, // 显示延迟(ms)
hideDelay: 100, // 隐藏延迟(ms)
// 位置控制
position: 'inside', // 'inside' | 'top' | 'bottom' | 函数
confine: false, // 是否限制在容器内
// 样式
backgroundColor: 'rgba(0,0,0,0.7)',
borderColor: '#333',
borderWidth: 0,
padding: 10,
textStyle: { color: '#fff' },
// 格式化
formatter: '{b}: {c}', // 字符串模板或函数
// 轴指示器
axisPointer: {
type: 'line', // 'line' | 'shadow' | 'cross' | 'none'
lineStyle: { color: '#999' }
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
自定义格式化:
javascript
tooltip: {
formatter: function(params) {
if (Array.isArray(params)) {
// axis 触发,多个系列
let html = `<strong>${params[0].name}</strong><br/>`;
params.forEach(param => {
html += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
});
return html;
} else {
// item 触发,单个系列
return `${params.name}<br/>${params.seriesName}: ${params.value}`;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Legend - 图例
javascript
legend: {
// 布局
orient: 'horizontal', // 'horizontal' | 'vertical'
left: 'center',
top: 'top',
// 数据
data: ['系列1', '系列2', '系列3'],
// 样式
textStyle: { color: '#333' },
itemWidth: 25, // 图例标记宽度
itemHeight: 14, // 图例标记高度
itemGap: 10, // 图例间距
// 交互
selectedMode: true, // 是否允许点击切换
selected: { // 初始选中状态
'系列1': true,
'系列2': false
},
// 分页(数据过多时)
type: 'scroll', // 'plain' | 'scroll'
pageButtonItemGap: 5,
pageButtonPosition: 'end'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DataZoom - 数据缩放
javascript
dataZoom: [
{
// 滑块型
type: 'slider',
show: true,
// 范围
start: 0, // 起始百分比(0-100)
end: 100, // 结束百分比
// 位置
left: '10%',
right: '10%',
bottom: 0,
height: 20,
// 样式
handleSize: '100%',
handleStyle: { color: '#5470c6' },
// 绑定轴
xAxisIndex: 0,
yAxisIndex: 0
},
{
// 内置型(鼠标滚轮)
type: 'inside',
start: 0,
end: 100,
xAxisIndex: 0
}
]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
VisualMap - 视觉映射
javascript
visualMap: {
// 类型
type: 'continuous', // 'continuous' | 'piecewise'
// 范围
min: 0,
max: 1000,
// 维度
dimension: 0, // 映射的数据维度
// 映射范围
inRange: {
color: ['#d94e5d', '#eac736', '#50a3ba'], // 颜色渐变
symbolSize: [10, 50], // 符号大小
opacity: [0.5, 1] // 透明度
},
// 控制条
calculable: true, // 是否显示拖拽手柄
orient: 'vertical',
left: 'right',
top: 'center'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
📝 代码示例
示例 1: 多坐标系对比图表
javascript
const option = {
title: { text: '双坐标系对比分析' },
// 两个独立的网格
grid: [
{ left: '5%', right: '55%', top: '15%', bottom: '10%' },
{ left: '55%', right: '5%', top: '15%', bottom: '10%' }
],
// 四个坐标轴
xAxis: [
{
gridIndex: 0,
type: 'category',
data: ['Q1', 'Q2', 'Q3', 'Q4'],
name: '季度'
},
{
gridIndex: 1,
type: 'category',
data: ['产品A', '产品B', '产品C', '产品D'],
name: '产品'
}
],
yAxis: [
{ gridIndex: 0, type: 'value', name: '销售额(万)' },
{ gridIndex: 1, type: 'value', name: '销量(件)' }
],
// 两个系列
series: [
{
name: '季度销售',
type: 'bar',
xAxisIndex: 0,
yAxisIndex: 0,
data: [120, 200, 150, 280],
itemStyle: { color: '#5470c6' }
},
{
name: '产品销量',
type: 'line',
xAxisIndex: 1,
yAxisIndex: 1,
data: [320, 280, 350, 420],
lineStyle: { color: '#91cc75', width: 2 }
}
],
// 共享的图例和提示框
legend: { data: ['季度销售', '产品销量'], top: 5 },
tooltip: { trigger: 'axis' }
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
示例 2: 复杂交互仪表盘
javascript
const option = {
title: { text: '销售监控仪表盘' },
// 交互组件
legend: {
data: ['销售额', '目标', '完成率'],
top: 30
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' }
},
toolbox: {
feature: {
dataView: { show: true, readOnly: false },
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
saveAsImage: { show: true }
}
},
dataZoom: [
{ type: 'slider', start: 0, end: 50 },
{ type: 'inside', start: 0, end: 50 }
],
visualMap: {
type: 'piecewise',
pieces: [
{ gt: 0, lte: 50, color: '#ee6666' },
{ gt: 50, lte: 80, color: '#fac858' },
{ gt: 80, color: '#91cc75' }
],
dimension: 1
},
// 坐标系
grid: { left: '3%', right: '4%', bottom: '10%', top: 80, containLabel: true },
xAxis: { type: 'category', data: dates },
yAxis: [
{ type: 'value', name: '金额' },
{ type: 'value', name: '完成率', max: 100, axisLabel: { formatter: '{value}%' } }
],
// 系列
series: [
{
name: '销售额',
type: 'bar',
data: salesData
},
{
name: '目标',
type: 'line',
data: targetData,
lineStyle: { type: 'dashed' }
},
{
name: '完成率',
type: 'line',
yAxisIndex: 1,
data: completionRate
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
⚠️ 常见问题
Q1: 组件不显示怎么办?
排查清单:
javascript
// 1. 检查组件是否正确配置
console.log(option.grid); // 是否存在
console.log(option.xAxis); // 是否存在
// 2. 检查索引是否正确
series: [{
xAxisIndex: 0, // 确保 xAxis[0] 存在
yAxisIndex: 0 // 确保 yAxis[0] 存在
}]
// 3. 检查坐标系绑定
series: [{
coordinateSystem: 'cartesian2d' // 默认值,可省略
}]
// 4. 检查数据是否为空
if (!data || data.length === 0) {
console.warn('数据为空,组件可能不显示');
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Q2: 多个组件冲突怎么办?
javascript
// 问题: 两个 grid 重叠
grid: [
{ left: '5%', right: '5%' }, // 重叠!
{ left: '5%', right: '5%' }
]
// 解决: 错开位置
grid: [
{ left: '5%', right: '55%' }, // 左半部分
{ left: '55%', right: '5%' } // 右半部分
]1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Q3: 如何动态添加/删除组件?
javascript
// 动态添加图例
chart.setOption({
legend: {
data: [...existingLegends, '新系列']
},
series: [
...existingSeries,
{ name: '新系列', type: 'bar', data: newData }
]
});
// 删除组件(设置为空)
chart.setOption({
legend: { data: [] }, // 清空图例
title: { show: false } // 隐藏标题
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🎯 最佳实践
1. 组件命名规范
javascript
// ✅ 好的做法: 语义化命名
grid: [
{ id: 'mainGrid', left: '5%' },
{ id: 'compareGrid', left: '55%' }
]
// ❌ 坏的做法: 无意义命名
grid: [
{ left: '5%' },
{ left: '55%' }
]1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2. 组件复用
javascript
// 提取通用配置
const baseGrid = {
containLabel: true,
backgroundColor: 'rgba(0,0,0,0.02)'
};
const baseTooltip = {
trigger: 'axis',
backgroundColor: 'rgba(0,0,0,0.7)'
};
// 复用
const option1 = {
grid: { ...baseGrid, left: '3%' },
tooltip: baseTooltip,
series: [...]
};
const option2 = {
grid: { ...baseGrid, left: '5%' },
tooltip: baseTooltip,
series: [...]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3. 性能优化
javascript
// 1. 减少不必要的组件
// ❌ 不需要图例时不要配置
legend: { data: [] } // 浪费资源
// ✅ 直接不配置
// 不写 legend
// 2. 大数据时简化交互
if (data.length > 10000) {
option.tooltip = { show: false }; // 关闭提示框
option.emphasis = { disabled: true }; // 关闭高亮
}
// 3. 避免频繁更新复杂组件
// 只更新数据,不更新组件结构
chart.setOption({
series: [{ data: newData }]
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
📊 性能指标
| 组件数量 | 初始化时间 | 内存占用 | 建议 |
|---|---|---|---|
| 简单(1-3个) | < 10ms | ~2MB | 无限制 |
| 中等(4-8个) | 10-30ms | ~5MB | 正常 |
| 复杂(9-15个) | 30-60ms | ~10MB | 注意优化 |
| 非常复杂(>15个) | > 60ms | > 15MB | 考虑拆分 |
🔗 相关链接
- 声明式配置体系
- setOption 更新机制
- 坐标系详解.md)
- 交互事件
- 官方组件文档
最后更新: 2026-04-23
难度等级: ⭐⭐⭐
预计阅读时间: 25 分钟
