1. 从一张“歪掉”的柱状图说起刚接触 ECharts 那会儿我总觉得柱状图是最没技术含量的图表——不就是几根柱子往那一戳吗直到有一次做运营数据看板产品经理指着屏幕问我“为什么这个月的柱子比上个月矮但数值明明更大”我盯着那张图看了半天才发现是 Y 轴起点没设成 0视觉上的高度差完全误导了判断。那一刻我才意识到柱状图的配置远不是“把数据塞进去”那么简单每一个参数背后都藏着对数据表达的思考。ECharts 的柱状图bar chart是数据可视化里出场率最高的图表类型之一它用矩形条的长度来映射数值大小直观、易读、对比性强。但正因为太常用很多人只记住了最基础的type: bar遇到分组、堆叠、横向、动态排序、大数据量渲染这些场景就开始抓瞎。这篇文章就是把我这些年踩过的坑、调过的参数、总结出来的配置套路系统地梳理一遍。不管你是刚上手 ECharts 的新手还是已经能画图但总被细节卡住的老手都能从里面找到能直接抄的配置和能避开的雷区。文章会从最基础的配置骨架讲起然后逐层深入到多系列、堆叠、横向、标签、坐标轴、交互、性能优化这些实战场景。每一段配置我都会解释“为什么这么设”而不是只丢一段代码让你复制。毕竟参数这东西理解了才能灵活变通死记硬背迟早会在某个奇怪的需求面前翻车。2. 柱状图配置的骨架先搞清楚谁在管什么2.1 一个最小可用配置的完整拆解很多人写 ECharts 配置是“拼图式”的——从文档里东抄一段西抄一段能跑就行。但这样做的后果是一旦图表出问题根本不知道从哪查起。我习惯先把一个最小配置拆成几个职责明确的模块这样后面加功能时心里有数。option { xAxis: { type: category, data: [一月, 二月, 三月, 四月, 五月] }, yAxis: { type: value }, series: [{ type: bar, data: [120, 200, 150, 80, 170] }] };这段代码里xAxis负责类目轴告诉图表“横着排哪些类别”yAxis负责数值轴决定“柱子长多高”series里的data才是真正的数值。三者通过数组下标一一对应——xAxis.data[0]对应series[0].data[0]这个对应关系是所有柱状图配置的根基。我见过有人把xAxis的type写成value结果柱子全挤在一起因为类目轴被当成了连续数值轴。也见过series里忘了写type: bar图表直接空白。这些低级错误看似可笑但在赶需求的时候真的会发生。所以我的建议是每次新建图表先把这三个模块的骨架搭好确认能出图再往上加东西。2.2 grid 与坐标轴柱子的“舞台”怎么搭grid这个配置项经常被忽略但它决定了绘图区域在容器里的位置。默认情况下ECharts 会自动留出边距但当你的轴标签很长、或者图例很多的时候默认边距就不够用了标签会被裁掉。grid: { left: 3%, right: 4%, bottom: 3%, top: 15%, containLabel: true }这里containLabel: true是个关键参数。它的作用是让 grid 的区域计算把坐标轴标签也算进去避免标签溢出被截断。我刚开始用的时候不知道这个参数每次都要手动调left和bottom的百分比调得头大。后来发现这个开关基本上设成true就能解决大部分标签被裁的问题。坐标轴这边axisLabel的rotate参数值得单独说。当类目名称很长、数量又多的时候标签会重叠。旋转 45 度是常见的解法xAxis: { axisLabel: { rotate: 45, interval: 0 } }interval: 0表示强制显示所有标签不自动隐藏。默认情况下 ECharts 会智能隐藏一部分标签来避免重叠但有时候隐藏得太多用户就看不出完整的类目了。配合旋转使用能在可读性和完整性之间找到平衡。2.3 柱子的宽度与间距barWidth 和 barGap 的配合柱子的宽度和间距直接影响图表的观感。太宽显得笨重太窄又看不清。ECharts 默认会根据类目数量自动计算宽度但自动计算的结果不一定符合你的预期。series: [{ type: bar, barWidth: 40%, barGap: 30%, barCategoryGap: 20%, data: [120, 200, 150, 80, 170] }]barWidth控制单根柱子的宽度可以写像素值如30也可以写百分比如40%相对于类目宽度。barGap控制同一类目下不同系列柱子之间的间距barCategoryGap控制不同类目之间的间距。这三个参数是联动的改一个往往要跟着调另外两个。我的经验是单系列柱状图用百分比设barWidth比较省心比如50%左右视觉上比较舒服。多系列的时候如果系列数量是 2 到 3 个barGap设成20%到30%之间柱子之间既不会挤在一起也不会散得太开。系列超过 4 个就要考虑是不是该换成堆叠或者别的图表类型了因为并排的柱子太多人眼很难对比。3. 多系列与堆叠从“并排”到“叠罗汉”的取舍3.1 多系列并排柱状图的配置要点多系列柱状图最常见的场景是对比比如“今年 vs 去年”的月度销售额。配置上就是在series数组里放多个对象series: [ { name: 今年, type: bar, data: [120, 200, 150, 80, 170] }, { name: 去年, type: bar, data: [100, 180, 130, 90, 140] } ]ECharts 会自动把同一类目下的柱子并排排列。但这里有个坑如果两个系列的数值量级差异很大比如一个是几百一个是几万那小的那组柱子几乎看不见。这时候要么用双 Y 轴要么考虑换成别的表达方式。双 Y 轴的配置需要给yAxis传数组并在series里用yAxisIndex指定yAxis: [ { type: value, name: 销售额 }, { type: value, name: 订单量 } ], series: [ { name: 销售额, type: bar, yAxisIndex: 0, data: [...] }, { name: 订单量, type: bar, yAxisIndex: 1, data: [...] } ]双 Y 轴虽然能解决量级问题但它有个隐患两条轴的刻度如果没对齐读者很容易产生误读。我的做法是尽量让两条轴的“零线”对齐并且给轴加上明确的name让读者知道哪根柱子对应哪条轴。3.2 堆叠柱状图stack 参数的正确用法堆叠柱状图用来展示“总量由哪些部分组成”配置上只需要给同一组的系列加上相同的stack值series: [ { name: 线上, type: bar, stack: 总量, data: [120, 200, 150] }, { name: 线下, type: bar, stack: 总量, data: [80, 100, 90] }, { name: 其他, type: bar, stack: 总量, data: [30, 50, 40] } ]stack的值是个标识符相同标识符的系列会叠在一起。这里有个容易忽略的点堆叠之后barWidth和barGap的行为会变化因为同一类目下只有一摞柱子了。这时候barGap基本不起作用barCategoryGap反而更重要它决定了不同类目之间那摞柱子的间距。堆叠柱状图最大的问题是“中间段难以比较”。比如三摞柱子叠在一起你想比较第二段的长度但它们的起点不一样肉眼很难判断。所以堆叠图更适合看“总量趋势”和“各部分占比的大致感觉”不适合做精确的段间对比。如果确实需要精确对比某一段可以考虑用“百分比堆叠”或者干脆拆成多个小图。3.3 堆叠与并排的混合一个真实需求的处理有一次做用户来源分析需求是“按渠道分组每个渠道内再区分新老用户”。这其实就是堆叠和并排的混合——渠道之间并排渠道内部堆叠。ECharts 本身不直接支持这种混合模式但可以通过给不同渠道设置不同的stack值来实现series: [ { name: 渠道A-新用户, type: bar, stack: 渠道A, data: [...] }, { name: 渠道A-老用户, type: bar, stack: 渠道A, data: [...] }, { name: 渠道B-新用户, type: bar, stack: 渠道B, data: [...] }, { name: 渠道B-老用户, type: bar, stack: 渠道B, data: [...] } ]这样渠道 A 的两组数据叠在一起渠道 B 的两组叠在一起而 A 和 B 之间是并排的。这个技巧的关键在于stack值的命名要能区分“组内”和“组间”。我一般用渠道名作为 stack 值这样逻辑清晰后面维护也不容易乱。4. 横向柱状图与动态排序让排名一目了然4.1 横向柱状图的配置转换横向柱状图在展示排名的时候比竖向更友好因为类目名称可以水平书写不用旋转。配置上就是把xAxis和yAxis的type对调xAxis: { type: value }, yAxis: { type: category, data: [类别A, 类别B, 类别C, 类别D] }, series: [{ type: bar, data: [120, 200, 150, 80] }]这里有个细节类目轴默认是从下往上排的也就是说data数组的第一个元素会显示在最下面。如果你想让排名第一的显示在最上面需要把data数组反转或者设置yAxis.inverse: true。yAxis: { type: category, inverse: true, data: [类别A, 类别B, 类别C, 类别D] }inverse: true会让类目轴从上往下排配合数值从大到小的数据就能得到“第一名在最上面”的效果。这个参数我在做排行榜的时候几乎每次都会用到。4.2 动态排序柱状图的实现思路动态排序柱状图也叫“赛跑图”是这几年比较火的一种可视化形式它能展示排名随时间的变化。ECharts 官方示例里有基于realtimeSort的实现但那个示例比较复杂。我这边说一个简化版的思路。核心是利用series的realtimeSort: true配合定时器更新数据series: [{ type: bar, realtimeSort: true, data: [...], label: { show: true, position: right } }]realtimeSort会让 ECharts 在数据更新时自动重新排序柱子的位置。你只需要在定时器里更新data图表就会动起来。但要注意这个功能对性能有一定要求数据量大的时候动画会卡。我的经验是控制在 10 到 15 个类目以内动画时长设成 300 到 500 毫秒看起来比较流畅。另外动态排序柱状图一定要加label因为柱子位置在变光看长度很难快速识别是哪个类目。标签放在柱子右侧横向图或者顶部竖向图都可以关键是让读者能跟上变化。4.3 排序带来的坐标轴陷阱动态排序的时候yAxis的data也需要跟着更新否则会出现“柱子动了但标签没动”的错位。我一开始做的时候只更新了series.data结果柱子排序变了但 Y 轴上的类目名还是原来的顺序整个图完全对不上。正确的做法是同时更新yAxis.data和series.data并且保证两者的顺序一致。如果用的是dataset数据源ECharts 会自动处理这个对应关系省心不少。所以做动态图表的时候我强烈建议用dataset而不是直接在series里写data。5. 标签、提示框与交互让图表“会说话”5.1 柱状图标签的显示策略柱状图的标签label默认是不显示的需要手动开启series: [{ type: bar, label: { show: true, position: top, formatter: {c} 件 }, data: [...] }]position可选top、inside、insideTop等。数值比较小、柱子比较矮的时候top可能会让标签超出绘图区域这时候可以用insideTop把标签放进柱子内部。标签的格式化用formatter支持字符串模板和回调函数。字符串模板里{a}是系列名{b}是类目名{c}是数值{d}是百分比。回调函数则更灵活可以根据数值大小返回不同的文本比如超过阈值就加个“↑”标记。我个人的习惯是如果柱子数量少少于 8 根就显示标签数量多的时候关掉标签靠 tooltip 来查看具体数值。因为标签太多会让图表显得杂乱反而影响阅读。5.2 tooltip 的定制化配置tooltip 是柱状图交互的核心默认的悬浮提示比较简陋通常需要定制tooltip: { trigger: axis, axisPointer: { type: shadow }, formatter: function(params) { let result params[0].axisValue br/; params.forEach(function(item) { result item.marker item.seriesName : item.value br/; }); return result; } }trigger: axis表示按坐标轴触发鼠标移到某个类目上时会显示该类目下所有系列的数据。axisPointer.type: shadow会显示一个阴影指示器比默认的线条更直观。formatter回调里的params是一个数组包含了当前类目下所有系列的信息。item.marker是系列对应颜色的圆点加上它能让提示框和图表颜色对应起来阅读体验好很多。有个细节要注意当trigger是axis时formatter的参数是数组当trigger是item时参数是单个对象。写回调的时候要区分清楚否则会报错。5.3 点击事件与图表联动柱状图的点击事件可以用来做下钻或者联动myChart.on(click, function(params) { console.log(params.name, params.value, params.seriesName); // 根据点击的类目加载详细数据 });params里包含了name类目名、value数值、seriesName系列名等信息。做下钻的时候通常会根据params.name去请求新的数据然后更新图表。图表联动则是多个图表之间通过dispatchAction来同步高亮状态myChart.on(click, function(params) { otherChart.dispatchAction({ type: highlight, seriesIndex: 0, dataIndex: params.dataIndex }); });这个在仪表盘场景里很实用点击一个图表的某根柱子另一个图表对应的数据也高亮起来帮助用户建立关联。6. 大数据量与性能柱子多了怎么办6.1 数据量增大时的渲染瓶颈柱状图在数据量小的时候性能很好但类目超过几百个之后渲染就会明显变慢。我做过一个测试500 根柱子的图表在普通笔记本上首次渲染大概要 1 秒多1000 根的时候要 3 秒以上而且交互会卡顿。瓶颈主要在两个地方一是 DOM 元素太多ECharts 虽然用的是 Canvas 渲染但每个柱子仍然有绘制开销二是标签和 tooltip 的计算量随数据量线性增长。6.2 大数据量下的配置优化针对大数据量有几个配置可以显著提升性能series: [{ type: bar, large: true, largeThreshold: 200, data: [...] }]large: true会开启大数据量优化模式ECharts 会用更简化的方式绘制柱子。largeThreshold是触发优化的阈值默认是 400我一般设成 200让优化更早生效。另外关掉不必要的动画和标签也能提升性能animation: false, series: [{ type: bar, label: { show: false }, data: [...] }]如果数据量实在太大比如上万条那柱状图本身就不是合适的表达方式了。这时候应该考虑聚合数据或者换成热力图、折线图等更适合大数据量的图表类型。6.3 数据采样与聚合的取舍有时候数据量太大但又必须用柱状图展示那就只能做聚合。比如按天统计的数据有 365 条可以聚合成按周或按月// 原始数据365 条日数据 // 聚合后12 条月数据 const monthlyData []; for (let i 0; i 12; i) { const monthData dailyData.slice(i * 30, (i 1) * 30); const sum monthData.reduce((acc, val) acc val, 0); monthlyData.push(sum); }聚合会损失细节但换来的是可读性和性能。我的原则是如果用户需要看趋势聚合到能看清趋势的粒度就行如果用户需要看具体某天的数据那就提供下钻功能点击月份柱子再展示该月的日数据。7. 那些年我踩过的柱状图配置坑7.1 Y 轴起点不为 0 导致的视觉误导前面提过这个坑这里再展开说一下。ECharts 的yAxis默认会从 0 开始但如果你手动设置了min或者数据里没有 0 附近的点ECharts 有时会自动调整起点。比如数据是[100, 102, 101, 103]Y 轴可能从 99 开始这样柱子之间的高度差会被放大看起来差异很大实际上只差几个点。yAxis: { type: value, min: 0 }强制min: 0是柱状图的基本原则。柱状图用长度映射数值如果起点不是 0长度就不成比例了。折线图可以不用从 0 开始因为折线看的是趋势和斜率但柱状图必须从 0 开始这是行业共识。7.2 类目轴数据顺序与 series 数据顺序不一致这个坑在动态更新数据的时候特别容易踩。比如你从接口拿到新数据类目顺序变了但series.data还是按旧顺序排的结果就是“张冠李戴”。// 错误做法只更新 series.data myChart.setOption({ series: [{ data: newData }] }); // 正确做法同时更新 xAxis.data 和 series.data myChart.setOption({ xAxis: { data: newCategories }, series: [{ data: newData }] });用dataset可以避免这个问题因为dataset里维度是绑定的ECharts 会自动处理对应关系。所以我现在做动态图表首选dataset。7.3 堆叠柱状图的负值处理堆叠柱状图遇到负值的时候表现会比较奇怪。正值往上叠负值往下叠但同一组里正负混合的时候视觉上会很混乱。series: [ { name: 收入, type: bar, stack: 总量, data: [100, -50, 80] }, { name: 支出, type: bar, stack: 总量, data: [-30, 60, -20] } ]这种数据用堆叠柱状图展示读者很难理解。我的建议是如果数据里有正有负要么拆成两个图表要么用别的图表类型。堆叠柱状图最适合的场景是“各部分都是正值且需要看总量”。7.4 图例与系列的对应关系错乱图例legend默认会根据series.name自动生成。如果series里没写name图例就会显示成“系列1”“系列2”这种默认名称用户根本看不懂。legend: { data: [今年, 去年] }, series: [ { name: 今年, type: bar, data: [...] }, { name: 去年, type: bar, data: [...] } ]legend.data里的名称要和series.name一一对应否则图例点击后不会正确隐藏对应的系列。我见过有人改了series.name但忘了改legend.data结果图例点了没反应查了半天才发现是名称对不上。8. 从配置到表达柱状图的设计思考8.1 颜色搭配的实用原则柱状图的颜色不是越花哨越好。单系列的时候用主题色就行多系列的时候颜色之间要有足够的区分度但也不能太刺眼。ECharts 内置了几套主题色直接设color数组就能用color: [#5470c6, #91cc75, #fac858, #ee6666, #73c0de]这套默认色板的区分度不错我一般直接用。如果需要自定义我的经验是相邻系列的颜色在色相上要拉开比如蓝配橙、绿配紫避免红配橙这种容易混淆的组合。另外堆叠柱状图的颜色最好有深浅关系同一组内用同色系的不同深浅视觉上能看出“这是一组”。8.2 标题与副标题的信息传达图表的标题不只是个名字它应该告诉读者“这张图在说什么”。ECharts 的title支持主标题和副标题title: { text: 2024年月度销售额, subtext: 数据来源内部统计系统, left: center }主标题点明主题副标题补充数据来源或时间范围。我习惯把副标题用上因为很多时候读者看到一张图第一反应是“这数据哪来的”“什么时候的”副标题能直接回答这些问题省去来回确认的沟通成本。8.3 响应式适配的注意事项ECharts 图表默认不会随容器大小变化而自动调整需要监听窗口的resize事件window.addEventListener(resize, function() { myChart.resize(); });如果图表在弹窗或者可折叠面板里容器大小变化时也要手动调用resize()。我遇到过图表在弹窗里显示不全的问题就是因为弹窗打开时容器尺寸还没稳定图表按旧尺寸渲染了。解决办法是在弹窗动画结束后延迟调用resize()或者用ResizeObserver监听容器变化。const observer new ResizeObserver(function() { myChart.resize(); }); observer.observe(document.getElementById(chart-container));ResizeObserver比window.resize更精准它能监听任意元素的大小变化不局限于窗口。这个在现代浏览器里支持度已经很好了我现在的项目基本都用它来做图表自适应。9. 一些零散但实用的配置技巧9.1 柱状图背景条的妙用showBackground: true可以给柱子加一个浅色背景条用来表示“满值”或者“目标值”series: [{ type: bar, showBackground: true, backgroundStyle: { color: rgba(180, 180, 180, 0.2) }, data: [...] }]这个在展示“完成率”的时候特别好用。背景条代表目标实际柱子代表完成量一眼就能看出差距。背景条的颜色要浅不能抢了主柱子的视觉焦点。9.2 圆角柱子的配置圆角柱子看起来更柔和配置也简单series: [{ type: bar, itemStyle: { borderRadius: [4, 4, 0, 0] }, data: [...] }]borderRadius的四个值分别对应左上、右上、右下、左下。竖向柱状图一般设上面两个角横向柱状图设右边两个角。圆角半径不要太大4 到 6 像素比较合适太大了柱子会显得“胖”。9.3 空数据与异常值的处理数据里难免有null或者NaNECharts 默认会把这些点跳过柱子不显示。但有时候你希望空数据也占个位置只是不画柱子series: [{ type: bar, data: [120, null, 150, NaN, 170], connectNulls: false }]connectNulls对柱状图其实没太大意义因为柱子之间本来就不连线。但知道这个参数的存在在处理折线图的时候会用得上。对于柱状图的空数据我的做法是在数据预处理阶段就把null转成 0 或者一个特殊标记避免图表上出现莫名其妙的空缺。9.4 导出图片与打印适配ECharts 提供了getDataURL方法来导出图片const imgUrl myChart.getDataURL({ type: png, pixelRatio: 2, backgroundColor: #fff });pixelRatio: 2能让导出的图片更清晰适合打印或者放到 PPT 里。backgroundColor一定要设否则导出的图片背景是透明的放到白色背景的文档里还好放到深色背景里就看不见了。打印适配的话主要是注意图表的尺寸和颜色。打印通常是黑白或者低色彩所以图表的颜色对比度要够高标签字号要够大。我一般会为打印单独准备一套配置把颜色改成灰度系字号调大 2 到 4 像素。10. 配置之外柱状图什么时候不该用说了这么多柱状图的配置最后想聊一个反向的问题什么时候不该用柱状图。柱状图适合“比较不同类目的数值大小”但如果类目太多超过 20 个柱子会挤成一片这时候横向柱状图或者条形图更合适。如果数据是连续的时间序列折线图更能体现趋势。如果是要看部分与整体的关系饼图或者矩形树图可能更直观。我见过有人用柱状图展示 100 个城市的销售额柱子细得像牙签标签全部重叠完全没法看。这种场景其实应该用“Top 10 其他”的方式聚合或者用地图来展示。图表类型的选择比配置细节更重要选错了类型配置调得再精细也是白搭。柱状图的配置看起来简单但真正用好需要理解每个参数背后的意图。我这些年最大的体会是不要为了配置而配置每一个参数调整都应该服务于“让读者更快更准地理解数据”这个目标。颜色、标签、坐标轴、交互所有细节都围绕这个目标来图表才不会沦为“好看但没用”的装饰。