Echarts动态排名柱状图实现:自适应多端与数据驱动动画 简介这是一份面向前端开发者与数据可视化初学者的ECharts动态排名柱状图实战源码聚焦解决多端适配与实时数据更新两大核心需求——既支持PC端大屏展示也兼容手机端触控交互适用于排行榜、业绩看板、舆情监测等业务场景。资源包共9个文件含3个关键JSecharts5.min.js、jquery-3.3.1.min.js、config.js、1个HTML主入口、1个CSS样式文件含响应式媒体查询、1个JSON模拟数据、1个XLS原始数据表、1个EXE启动工具及1份中文说明文档整体6.87MB结构清晰、开箱即用。已有3783人学习下载读者可直接运行start.exe快速预览效果通过config.js掌握动态排序逻辑与setInterval驱动机制结合data.json与lockdata.xls理解数据源组织方式并借助common.css中的media规则深入响应式布局实践是贯通ECharts配置、异步更新与跨端适配的典型教学案例。1. 项目概述为什么我们需要一个自适应的动态排名柱状图在数据可视化的日常开发中我们经常遇到一个经典场景需要展示一组随时间变化或按条件排序的排名数据。比如实时销售榜单、系统性能指标排行、社交媒体热度趋势等。一个静态的柱状图或许能展示某一时刻的快照但缺乏“动态”和“排名”这两个关键元素信息的冲击力和叙事感就大打折扣。更棘手的是如今用户访问设备的多样性——从大屏桌面显示器到小巧的手机屏幕——要求我们的图表必须具备完美的自适应能力否则在移动端上看到的可能就是一堆挤在一起、难以辨认的图形和文字。这正是“Echarts动态排名柱状图自适应电脑和手机端”这个项目要解决的核心问题。它不是一个简单的图表展示而是一个集成了数据驱动动画、智能布局响应和流畅交互体验的完整解决方案。我之所以花时间打磨这套源代码是因为在实际项目中我发现直接使用Echarts基础配置来实现这些特性会散落各处代码难以维护尤其是在需要快速复用和适配不同业务场景时。因此我将核心逻辑封装起来形成了一套开箱即用、配置灵活的方案。简单来说这个项目能帮你快速构建一个这样的图表它像体育比赛中的实时积分榜柱子会随着数据更新而动态增长、缩短项目的排名也会随之上下跳动。同时无论用户是在27寸的4K显示器上还是在6英寸的手机屏幕上查看图表都能自动调整尺寸、字体大小、图例位置等确保最佳的可读性和交互性。对于前端开发者、数据分析师或是需要制作动态报告的产品经理来说这无疑是一个能极大提升效率和视觉表现力的工具。2. 核心设计思路与架构拆解2.1 “动态排名”的实现机理动态排名的核心在于“数据驱动”和“动画过渡”。它不仅仅是数据更新后重新绘制图表而是要让整个变化过程平滑、直观让用户清晰地看到每个数据项是如何竞争、排位如何更迭的。数据模型与排序逻辑 底层数据通常是一个对象数组每个对象包含name名称、value数值以及可能的category类别等属性。动态排名的第一步是在每次数据更新时根据value对数组进行降序排序并计算出每个数据项的新排名rank。这里的关键是我们需要记录上一次的排名以便在动画中体现排名的变化上升、下降或保持。动画策略 Echarts本身提供了强大的动画配置。对于柱状图我们可以利用animationDuration、animationEasing和animationDelay来控制柱子高度变化的动画。但实现“排名变化”的视觉提示需要一些技巧。一个常见的做法是在Y轴类目轴上让每个类目标签即数据项名称的位置也产生动画。我们可以通过对比新旧数据的排名为每个数据项计算一个动画延迟animationDelay排名上升多的项可以稍早开始动画形成一种“你追我赶”的视觉效果。另一种更直观的方式是在柱子上或旁边添加一个排名变化的箭头图标和数字通过graphic组件或自定义series.label来实现。定时更新与数据流 动态意味着持续变化。我们需要一个机制来定时或实时地获取新数据并更新图表。这通常通过setInterval模拟数据更新或者通过WebSocket连接真实数据源。在更新时不是粗暴地调用setOption重置整个图表而是使用Echarts的setOption方法并传入notMerge: false默认或精心管理新旧option的差异以实现最小化的动画重绘保证性能。2.2 “自适应多端”的响应式方案自适应布局是前端开发的老话题但在Echarts中实现完美的自适应需要兼顾容器、配置项和交互。容器级自适应 这是基础。图表的DOM容器必须使用响应式宽度例如width: 100%。同时需要监听浏览器窗口的resize事件并在事件触发时调用Echarts实例的resize()方法。为了性能通常会对resize事件进行防抖处理。// 防抖函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later () { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout setTimeout(later, wait); }; } // 初始化图表 const myChart echarts.init(document.getElementById(chart-container)); // 防抖处理的重置大小函数 const resizeHandler debounce(() { myChart.resize(); }, 250); window.addEventListener(resize, resizeHandler);配置项级自适应 这是关键也是难点。不同的屏幕尺寸下理想的字体大小、图例位置、网格边距、柱子宽度等都不同。媒体查询Media Query思路 我们可以在初始化或每次resize时根据当前容器的宽度myChart.getWidth()来判断设备类型然后动态生成或切换一套对应的配置项option。例如当宽度小于768px时我们判定为移动端此时可以缩小title.textStyle.fontSize和axisLabel.fontSize。将legend从顶部或右侧改为底部并设置为水平排列orient: horizontal。调整grid网格的top、bottom、left、right边距为可能缩小的图表区域腾出空间。增加barWidth柱子宽度或使用barCategoryGap来确保在狭窄空间内柱子仍然清晰可辨。比例缩放思路 另一种更精细的方法是以一个基准设计尺寸如1920px桌面端的配置为蓝本根据当前容器宽度与基准宽度的比例动态计算所有尺寸相关的属性字体、边距、图形大小等。这种方法能实现更平滑的过渡但计算逻辑稍复杂。交互自适应 在移动端触摸是主要交互方式。可能需要考虑禁用一些在移动端不友好的组件如dataZoom数据区域缩放的滑块或将其替换为触屏友好的版本。确保tooltip提示框在移动端能正常触发和显示有时需要调整其position策略为fixed跟随手指。注意 自适应配置的切换最好在调用myChart.setOption(newOption, { notMerge: false })时完成。notMerge: false意味着新的option会与旧的合并这通常是我们需要的。但要注意对于某些需要完全重置的配置如从垂直图例切换到水平图例可能需要更精细的控制或使用notMerge: true后重新设置全部选项。2.3 技术选型为什么是Echarts实现动态排名柱状图市面上有D3.js、Chart.js、Highcharts等多个优秀的库。我选择Echarts是基于以下几个在实际项目中反复验证过的优势动画系统强大且易用 Echarts内置的动画系统非常成熟只需简单配置即可实现平滑的图形变换、颜色过渡这对于实现“动态排名”中的柱子高度变化、颜色渐变至关重要无需自己从零实现复杂的动画逻辑。丰富的配置项与API Echarts提供了极其详尽的配置项几乎能控制图表的每一个像素。这对于实现复杂的自适应布局如精确控制各组件在不同屏幕下的位置和样式提供了可能。其清晰的API文档也让开发和调试过程更顺畅。性能优异 对于数据量不是极端庞大的动态更新场景Echarts基于Canvas的渲染方式性能表现良好能保持流畅的动画帧率。其setOption的差异合并机制也能有效减少不必要的重绘。社区活跃与生态丰富 百度开源的Echarts拥有庞大的用户社区和丰富的示例遇到问题时更容易找到解决方案或灵感。同时其与Vue、React等主流框架的集成组件如vue-echarts、echarts-for-react也相当成熟便于在工程化项目中使用。3. 核心代码实现与分步解析下面我将拆解实现自适应动态排名柱状图的核心代码模块。为了清晰我将代码分为初始化、配置生成、数据更新和自适应处理四个部分。3.1 图表初始化与容器准备首先我们需要一个HTML容器并引入Echarts库。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title自适应动态排名柱状图/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script style #rankChart { width: 100%; height: 600px; /* 初始高度自适应时会通过JS调整 */ } /* 移动端样式覆盖 */ media (max-width: 768px) { #rankChart { height: 400px; } } /style /head body div idrankChart/div script srcmain.js/script !-- 我们的主逻辑代码 -- /body /html在main.js中我们进行初始化。这里的关键是获取容器、初始化实例并绑定窗口大小改变事件。// main.js (function() { // 获取图表容器 const chartDom document.getElementById(rankChart); if (!chartDom) return; // 初始化Echarts实例 const myChart echarts.init(chartDom); // 初始数据示例 let currentData [ { name: 产品A, value: 135, rank: 1 }, { name: 产品B, value: 120, rank: 2 }, { name: 产品C, value: 98, rank: 3 }, { name: 产品D, value: 76, rank: 4 }, { name: 产品E, value: 65, rank: 5 }, { name: 产品F, value: 50, rank: 6 }, ]; // 当前设备类型标识用于自适应配置 let isMobile window.innerWidth 768; // 防抖函数用于resize事件 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later () { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout setTimeout(later, wait); }; } // 响应式调整函数 function handleResize() { const newIsMobile window.innerWidth 768; // 如果设备类型发生变化重新生成配置并设置 if (newIsMobile ! isMobile) { isMobile newIsMobile; const option generateChartOption(currentData, isMobile); myChart.setOption(option, { notMerge: true }); // 设备类型变化建议不合并 } else { // 仅容器大小变化调用resize即可 myChart.resize(); } } // 绑定防抖后的resize事件 const debouncedResize debounce(handleResize, 200); window.addEventListener(resize, debouncedResize); // 初始渲染 const initialOption generateChartOption(currentData, isMobile); myChart.setOption(initialOption); // 模拟动态数据更新示例每3秒更新一次 setInterval(() { updateChartData(myChart); }, 3000); // 将图表实例暴露给全局方便调试可选 window.myChart myChart; })();3.2 动态生成自适应的配置项Option这是项目的核心逻辑所在。generateChartOption函数根据传入的数据和设备类型动态生成Echarts配置。/** * 生成Echarts配置项 * param {Array} data - 图表数据格式为[{name, value, rank, ...}] * param {Boolean} isMobile - 是否为移动端 * returns {Object} Echarts配置对象 */ function generateChartOption(data, isMobile) { // 基础颜色配置 const colorList [#5470c6, #91cc75, #fac858, #ee6666, #73c0de, #3ba272, #fc8452, #9a60b4, #ea7ccc]; // 根据设备类型定义尺寸基准 const baseFontSize isMobile ? 12 : 14; const titleFontSize isMobile ? 16 : 20; const legendFontSize isMobile ? 12 : 14; const barWidth isMobile ? 60% : 40%; // 移动端柱子相对宽一些 // 准备系列数据按排名排序后映射 const sortedData [...data].sort((a, b) b.value - a.value); const categories sortedData.map(item item.name); const values sortedData.map(item item.value); // 构建配置对象 const option { // 标题 title: { text: 实时产品销量排名, left: center, textStyle: { fontSize: titleFontSize, fontWeight: normal } }, // 提示框 tooltip: { trigger: axis, axisPointer: { type: shadow }, formatter: function(params) { const dataItem params[0]; return ${dataItem.name}br/排名: ${dataItem.dataIndex 1}br/数值: ${dataItem.value}; } }, // 图例在动态排名中图例可能不是必须的这里作为示例 legend: { show: !isMobile, // 移动端隐藏图例以节省空间 top: isMobile ? undefined : 30, bottom: isMobile ? 0 : undefined, left: center, orient: isMobile ? horizontal : vertical, textStyle: { fontSize: legendFontSize } }, // 网格区域控制绘图区域的位置和大小 grid: { top: isMobile ? 50 : 80, bottom: isMobile ? 70 : 90, left: isMobile ? 60 : 80, right: isMobile ? 20 : 100, // 右侧留出空间显示排名变化标签 containLabel: true // 确保坐标轴标签在grid内 }, // X轴数值轴 xAxis: { type: value, axisLabel: { fontSize: baseFontSize, formatter: {value} // 可以在这里格式化数值如加单位 }, splitLine: { lineStyle: { type: dashed } } }, // Y轴类目轴显示产品名称 yAxis: { type: category, data: categories, axisLabel: { fontSize: baseFontSize, interval: 0 // 强制显示所有标签 }, axisTick: { show: false }, axisLine: { show: true }, // 实现Y轴标签动画的关键根据新数据顺序设置 inverse inverse: true, // 让排名第一的显示在最上面 }, // 系列定义 series: [ { name: 销量, type: bar, data: values.map((value, index) ({ value: value, // 每个数据项可以携带自定义样式或信息 itemStyle: { color: colorList[index % colorList.length], borderRadius: [0, 5, 5, 0] // 柱子右侧圆角 }, // 标签配置显示在柱子右侧 label: { show: true, position: right, fontSize: baseFontSize, fontWeight: bold, formatter: function(params) { // 可以在这里计算并显示排名变化 // 例如{c} (2) 表示数值和上升2名 // 这里简化只显示数值 return params.value; } } })), barWidth: barWidth, // 动画配置 animation: true, animationDuration: 1000, animationEasing: cubicOut, // 为每个柱子设置不同的动画延迟营造依次动画效果 animationDelay: function (idx) { return idx * 100; // 每个柱子比前一个晚100ms开始动画 }, } ], // 全局动画配置 animationDuration: 1000, animationEasing: cubicInOut, }; // 如果不是移动端可以添加更丰富的组件如数据区域缩放 if (!isMobile) { option.dataZoom [ { type: slider, yAxisIndex: 0, filterMode: empty, start: 0, end: 100 } ]; } return option; }3.3 实现数据动态更新与排名计算动态排名的灵魂在于数据的持续更新和随之而来的排名重计算与平滑过渡。/** * 模拟数据更新并刷新图表 * param {ECharts} chartInstance - Echarts实例 */ function updateChartData(chartInstance) { // 1. 获取当前图表数据这里从我们维护的currentData变量获取实际项目可能从API获取 let oldData window.currentData || []; // 假设我们有一个全局变量存储上次数据 // 2. 模拟生成新数据在实际应用中这里替换为API调用 const newData oldData.map(item { // 为每个值增加一个随机增量或减量模拟数据波动 const change (Math.random() - 0.5) * 20; // -10 到 10 之间的变化 let newValue item.value change; newValue Math.max(0, Math.round(newValue * 10) / 10); // 确保非负保留一位小数 return { name: item.name, value: newValue }; }); // 3. 计算新旧排名 const oldDataWithRank calculateRank([...oldData]); const newDataWithRank calculateRank([...newData]); // 4. 为每个新数据项添加上次排名信息用于在标签中显示排名变化 const dataForChart newDataWithRank.map(newItem { const oldItem oldDataWithRank.find(d d.name newItem.name); const rankChange oldItem ? oldItem.rank - newItem.rank : 0; // 正数表示上升负数表示下降 return { ...newItem, rankChange: rankChange }; }); // 5. 更新全局数据变量 window.currentData dataForChart; // 6. 获取当前设备类型生成新配置 const isMobile window.innerWidth 768; const newOption generateChartOption(dataForChart, isMobile); // 7. 关键步骤设置新选项触发动画。 // 注意这里使用 notMerge: false (默认)Echarts会智能地合并新旧option只更新变化的部分从而触发平滑动画。 chartInstance.setOption(newOption); // 可选在控制台输出变化便于调试 console.log(数据已更新, dataForChart); } /** * 根据数值计算排名 * param {Array} data - 数据数组包含value属性 * returns {Array} 添加了rank属性的新数组 */ function calculateRank(data) { // 深拷贝并排序 const sorted [...data].sort((a, b) b.value - a.value); // 处理并列排名 const rankedData []; let currentRank 1; for (let i 0; i sorted.length; i) { if (i 0 sorted[i].value ! sorted[i - 1].value) { currentRank i 1; } rankedData.push({ ...sorted[i], rank: currentRank }); } // 按照原始名称顺序返回可选这里按排名后顺序返回 return rankedData; }为了让排名变化更直观我们可以优化generateChartOption中系列的label.formatter将排名变化显示出来// 在generateChartOption函数的series.data的label.formatter中 formatter: function(params) { const dataIndex params.dataIndex; const dataItem sortedData[dataIndex]; // sortedData是外部变量需要能访问到 const value params.value; let changeText ; if (dataItem.rankChange ! undefined) { if (dataItem.rankChange 0) { changeText ↑${dataItem.rankChange}; } else if (dataItem.rankChange 0) { changeText ↓${Math.abs(dataItem.rankChange)}; } else { changeText →; } } return ${value} ${changeText}; }实操心得 在动态更新数据时setOption的合并策略是关键。默认的合并行为在大多数情况下工作良好但如果你自定义了复杂的图形元素如通过graphic添加的箭头可能需要更精细的控制甚至考虑在更新前用clear()清空再重绘。对于简单的柱状图数据更新默认合并是最高效的方式。3.4 深入自适应细节字体、间距与交互优化上面的代码已经实现了基本的自适应但在真实项目中我们还需要打磨更多细节。字体大小的阶梯式响应 简单的isMobile布尔判断可能不够。我们可以根据容器宽度设置多个断点实现更精细的控制。function getResponsiveConfig(containerWidth) { if (containerWidth 1200) { return { baseSize: 16, titleSize: 24, legendOrient: vertical }; } else if (containerWidth 768) { return { baseSize: 14, titleSize: 20, legendOrient: vertical }; } else if (containerWidth 480) { return { baseSize: 13, titleSize: 18, legendOrient: horizontal }; } else { // 手机竖屏 return { baseSize: 12, titleSize: 16, legendOrient: horizontal }; } }Y轴标签截断与提示 在移动端产品名称可能很长导致Y轴标签重叠。我们可以设置axisLabel.formatter来截断文本并用tooltip显示全名。yAxis: { // ... other config axisLabel: { fontSize: baseFontSize, formatter: function(value) { // 移动端截断长文本 if (isMobile value.length 6) { return value.substring(0, 5) ...; } return value; } } }同时确保tooltip的formatter能显示完整的名称。移动端触摸交互优化 在移动端为了更好的触摸体验可以增加柱子的触摸区域并优化tooltip的显示。series: [{ // ... other config // 增加emphasis高亮样式让触摸反馈更明显 emphasis: { itemStyle: { shadowBlur: 10, shadowColor: rgba(0, 0, 0, 0.5) } }, // 在移动端可以稍微加宽柱子便于点击 barWidth: isMobile ? 70% : barWidth, }]性能考虑 在频繁更新图表的场景下需要注意在window.resize时使用防抖避免频繁调用resize和setOption。如果数据系列非常多例如超过50个需要考虑使用large: true开启大数据模式或者对数据进行采样展示。在组件销毁时如单页应用路由切换务必调用myChart.dispose()释放资源并移除resize事件监听器防止内存泄漏。4. 常见问题、排查技巧与优化实录在实际开发和调试这套动态排名柱状图的过程中我遇到了不少典型问题。这里将它们整理出来并附上我的解决思路希望能帮你绕过这些坑。4.1 动画不流畅或卡顿问题描述 数据更新时柱子动画生硬、卡顿或者整个图表区域闪烁。排查与解决检查数据量 首先确认数据项是否过多。Echarts在处理大量图形元素动画时会有压力。如果数据项超过100考虑是否真的需要全部显示或者启用large模式series-large。优化setOption调用 确保没有在极短的时间间隔内如每秒多次连续调用setOption。如果数据源更新很快可以设置一个更新节流阀比如每秒最多更新一次图表。简化配置项 检查option中是否包含了不必要的复杂配置比如过于精细的itemStyle渐变、过多的graphic自定义图形。在动态更新时尽量保持配置简洁。使用Canvas渲染器 Echarts默认使用Canvas渲染性能通常比SVG渲染器更好尤其是在动态场景下。确保你没有强制使用SVG渲染器renderer: svg。浏览器开发者工具性能分析 使用Chrome DevTools的Performance面板录制动画过程查看是否有长时间的任务阻塞了主线程。4.2 自适应切换时布局错乱问题描述 从电脑端切换到手机端或反之图表布局如图例位置、标题偏移出现错位、重叠。排查与解决notMerge参数使用不当 这是最常见的原因。当设备类型发生根本变化如从桌面布局切换到移动布局很多配置项如legend的orient、grid的边距是互斥的。此时使用默认的notMerge: false合并可能导致新旧配置冲突。我的经验是在检测到设备类型变化时使用notMerge: true完全重置配置如上面handleResize函数所示。配置计算逻辑错误 仔细检查generateChartOption函数中所有依赖于isMobile的配置项计算是否正确。特别是grid的top、left等属性单位是像素还是百分比要确保一致。容器尺寸获取时机 在resize事件触发后立即获取chartDom.offsetWidth可能得到的是旧值。Echarts的resize()方法是异步的。更可靠的做法是在resize事件处理函数中先调用myChart.resize()然后在下一次动画帧requestAnimationFrame中再获取宽度并计算新配置。function handleResize() { myChart.resize(); // 先让图表适应新容器大小 requestAnimationFrame(() { const newWidth myChart.getWidth(); const newIsMobile newWidth 768; if (newIsMobile ! isMobile) { isMobile newIsMobile; const option generateChartOption(currentData, isMobile); myChart.setOption(option, { notMerge: true }); } }); }4.3 排名变化箭头或标签显示异常问题描述 自定义的排名变化箭头↑↓不显示、位置不对或者数字计算错误。排查与解决数据关联错误 确保在updateChartData函数中正确地将rankChange属性关联到了对应的数据项上。使用console.log打印dataForChart检查每个对象的rankChange值是否符合预期正数为上升。formatter函数作用域 在label.formatter或graphic的formatter中你访问的sortedData或dataItem可能不在当前作用域内。确保这些变量在函数被定义时是可访问的或者通过params参数传递进去。在上面的示例中我将计算逻辑移到了formatter外部通过dataIndex从预先处理好的数据中查找。图形元素位置计算 如果使用graphic元素绘制箭头其位置position: [x, y]需要根据柱子位置动态计算。这通常需要在setOption后通过myChart.convertToPixel(grid, [value, category])API将数据坐标转换为像素坐标。这个过程相对复杂且容易在数据更新时出错。对于简单的变化指示我更推荐使用label的formatter来添加文本符号如上例所示这样更简单稳定。4.4 移动端Tooltip触发不灵敏或位置不佳问题描述 在手机触摸时tooltip很难触发或者弹出位置遮挡了关键信息。排查与解决确保trigger为axis或item 对于柱状图axis通常体验更好手指触摸到该系列的任何位置都会触发。调整position 默认的tooltip位置可能不适合小屏幕。可以自定义position函数将其定位在手指上方且不超出屏幕。tooltip: { trigger: axis, position: function (point, params, dom, rect, size) { // point: 鼠标位置 // size: tooltip的大小 {width, height} // dom: tooltip的DOM元素 const x point[0]; const y point[1]; const viewWidth window.innerWidth; const viewHeight window.innerHeight; let tipX x; let tipY y; // 如果tooltip右边超出屏幕向左移动 if (x size.width viewWidth) { tipX x - size.width - 10; } // 如果tooltip底部超出屏幕向上移动移动端常见 if (y size.height viewHeight) { tipY y - size.height - 10; } return [tipX, tipY]; }, // ... 其他配置 }3. **考虑使用touchstart事件模拟** 在极端情况下可以监听图表的touchstart事件手动调用myChart.dispatchAction({ type: showTip, ... })来显示tooltip但这需要更精细的坐标计算。4.5 在Vue/React等框架中集成时的注意事项问题描述 在Vue或React组件中使用时图表不更新、内存泄漏或多次初始化。排查与解决生命周期管理 在Vue的mounted或React的useEffect依赖项为空数组中初始化图表。在组件销毁前Vue的beforeUnmount React的useEffect清理函数务必调用myChart.dispose()并移除全局事件监听如resize。响应式数据更新 当组件的数据props或state变化时在相应的侦听器或useEffect中调用setOption更新图表。关键点 确保传入setOption的是全新的option对象而不是修改后的旧对象引用以触发Echarts的响应式更新。在Vue中对于复杂对象可能需要使用JSON.parse(JSON.stringify(...))进行深拷贝。使用官方封装库 强烈推荐使用vue-echarts或echarts-for-react这类官方推荐的集成库。它们已经妥善处理了生命周期、响应式更新和性能优化能避免很多底层问题。避免重复初始化 确保初始化图表的代码只执行一次。可以在组件实例上保存一个标记或者利用框架的ref来存储图表实例。一个Vue 3 script setup的简单示例template div refchartRef stylewidth: 100%; height: 500px;/div /template script setup import { ref, onMounted, onUnmounted, watch } from vue; import * as echarts from echarts; const chartRef ref(null); let myChart null; const props defineProps({ chartData: Array, isMobile: Boolean }); // 初始化 onMounted(() { if (chartRef.value) { myChart echarts.init(chartRef.value); updateChart(); window.addEventListener(resize, handleResize); } }); // 响应数据变化 watch(() [props.chartData, props.isMobile], () { if (myChart) { updateChart(); } }, { deep: true }); // 清理 onUnmounted(() { if (myChart) { window.removeEventListener(resize, handleResize); myChart.dispose(); myChart null; } }); function updateChart() { const option generateChartOption(props.chartData, props.isMobile); // 复用前面的函数 myChart.setOption(option); } function handleResize() { myChart myChart.resize(); } /script这套自适应动态排名柱状图的源代码从构思到实现再到解决上述各种实际问题花费了不少心血。其价值在于将Echarts的强大功能与复杂的业务需求动态、排名、自适应进行了封装和抽象提供了一个相对高层次的、可复用的解决方案。你可以直接拿过去替换数据源和少量的样式配置就能快速集成到自己的项目中。无论是用于后台数据监控大屏还是面向公众的移动端H5活动页它都能提供专业、流畅的视觉体验。本文还有配套的精品资源点击获取