ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ECharts交互组件封装实战:从基础图表到地图3D与性能优化

ECharts交互组件封装实战:从基础图表到地图3D与性能优化 做后台管理系统和数据可视化大屏的人基本都绕不开 ECharts。我最早碰 ECharts 的时候每个页面都复制一套 option代码里塞满了图表初始化、窗口 resize、数据请求这些重复逻辑后来花了两周时间把散落各处的图表逻辑收敛成一套 ECharts 交互组件才真正把“一次性画图”变成了“可复用的交互零件”。这篇东西不是官方文档的翻译而是这几年在多个项目里踩坑踩出来的实践总结涉及组件封装、事件联动、地图与 3D、跨端集成和问题排查适合刚学会 echarts.init 的初级前端也适合想统一团队图表规范的朋友参考。1. 交互组件第一步把 ECharts 封装成“会用不炸”的基础组件1.1 为什么一定要组件化一张图胜过十个 option 配置很多初学者觉得 ECharts 就是找个 div然后 echarts.init setOption 两步走。确实单张图这么写一点问题没有但项目一复杂问题就来了。我接手过一个报表系统一屏十张图每张图都是单独页面复制粘贴的代码。乍一看能跑实际上存在三个隐患第一init 逻辑散落窗口 resize 的监听器到处都是页面跳转时监听器不清除图表实例也不 dispose浏览器内存稳步上涨第二图表配置和业务数据混在一起改一个主题色要全局搜索替换改一次坐标轴名称要同时改七八个文件第三新同事接手时不知道哪些代码能删、哪些不能删只能一路复制粘贴下去。组件化解决的就是这三个问题。把“创建实例、设置配置、响应数据、处理 resize、销毁释放”这五件事收敛到一个组件里业务侧只关心一件事我要展示的数据和交互效果是什么。组件不关心数据从哪来父组件也不关心 canvas 是怎么画出来的两边通过 option 这一个对象解耦。这种模式在 Vue 2、Vue 3、React 里都通用只是语法略有差异。1.2 一个初始化组件的完整骨架我用的是 Vue 3 TypeScript 技术栈下面是一个简化后的 BaseChart 组件核心逻辑完整可跑。它支持传入 option、高度、主题名并在组件卸载时自动销毁图表实例。template div refchartRef classbase-chart :style{ height: height px }/div /template script setup langts import * as echarts from echarts import { ref, onMounted, onBeforeUnmount, watch, nextTick } from vue const props defineProps{ option: Recordstring, any height?: number theme?: string }() const chartRef refHTMLDivElement() let chart: echarts.ECharts | null null let resizeObserver: ResizeObserver | null null function initChart() { if (!chartRef.value) return chart echarts.init(chartRef.value, props.theme || default) chart.setOption(props.option, { notMerge: false }) } function updateChart() { if (!chart) return chart.setOption(props.option, { notMerge: false }) } function resizeChart() { chart?.resize() } onMounted(async () { await nextTick() initChart() // 优先使用 ResizeObserver容器尺寸变化时自动适配 if (window.ResizeObserver chartRef.value) { resizeObserver new ResizeObserver(resizeChart) resizeObserver.observe(chartRef.value) } else { window.addEventListener(resize, resizeChart) } }) watch(() props.option, updateChart, { deep: true }) onBeforeUnmount(() { resizeObserver?.disconnect() window.removeEventListener(resize, resizeChart) chart?.dispose() chart null }) /script这里有个比较隐蔽的细节watch option 时用deep: true但 option 对象一旦很大比如地图系列的 geo 数据、树图的节点数据deep 监听会带来额外的性能开销。实际项目中我建议两种方式二选一要么父组件保证传入的 option 是稳定引用只有数据变化时才重新生成对象要么给组件传一个version或updateKey字段组件 watch 这个字段再更新既简单又高效。1.3 resize、销毁、主题切换这些“隐形交互”才是体验分水岭图表交互不只是鼠标点一点、hover 一下窗口拉伸、容器尺寸变化、主题切换、数据刷新这些都是交互的一部分而且往往是体验分水岭。先讲 resize。很多人用 window.resize 事件但在后台系统里图表的容器宽度往往由侧边栏折叠、栅格布局、卡片展开等因素决定window 大小没变容器却变了。用 ResizeObserver 监听容器才是正解。注意监听回调要防抖一般 200ms 就够了否则窗口拖动时图表会连续触发多次重绘性能差的机器能明显感觉到卡顿。再讲主题切换。ECharts 注册了 dark 主题后如果想动态切换最简单粗暴的办法是调用chart.dispose()后重新 init 并 setOption。这里有个体验细节切换主题前应该保存当前的dataZoom位置、图例选中状态、当前工具栏状态重新渲染后恢复否则用户一换主题之前筛选的状态全部丢失体验很割裂。最后讲提示框。tooltip是交互组件里最容易被忽略的部分我见过太多图表鼠标移到数据点上提示框被容器边框切掉一半。处理办法是confine: true让 tooltip 始终在容器内enterable: true允许鼠标移入 tooltip 区域再配合trigger: axis多折线对比时体验提升非常明显。2. 不只是画图事件交互与联动控制的实战拆解2.1 点击事件从“打开控制台”到“真正能点”ECharts 的事件系统非常强大但很多人只停留在chart.on(click, ...)这一个用法上。有一次我需要给折线图接一个下钻功能点击某个点要查详情、跳到另一个页面来回传参数。这种需求光靠组件封装还不够关键在于事件回调里能拿到什么。chart.on(click, (params) { console.log(componentType:, params.componentType) console.log(seriesType:, params.seriesType) console.log(seriesIndex:, params.seriesIndex) console.log(dataIndex:, params.dataIndex) console.log(value:, params.value) console.log(name:, params.name) })点击一个柱子的柱身、点击一个散点、点击一个地图区块params 里字段会略有差异。实际项目里我踩过一个坑图例也可以触发 click如果不加判断直接按params.seriesType处理会在切换图例时误触发业务逻辑。所以我习惯在回调第一行判断params.componentType series再往下走业务。点击事件还有一个进阶玩法是用dispatchAction模拟用户操作比如通过代码控制高亮chart.dispatchAction({ type: highlight, seriesIndex: 0, dataIndex: 3 }) chart.dispatchAction({ type: showTip, seriesIndex: 0, dataIndex: 3 })这在引导用户关注某些数据点、或者做全屏轮播“自动讲故事”的大屏场景里非常好用。2.2 父子与跨组件联动把图表变成可对话的部件Vue 3 项目中父子组件交互是最常规的。父组件给子组件传 option子组件通过emit(chartClick, params)把点击事件抛出去。但真实业务里图表和图表之间往往不是父子关系而是一个页面两侧两个独立区域左侧是产品类目柱状图右侧是每个类目的销售趋势折线图点击左侧柱状图的某个类目右侧必须刷新数据。这种“非父子组件交互”我推荐优先用 Pinia 或 Vuex 这类状态管理而不是随便搞个 eventBus。原因很简单eventBus 的优点是“快”缺点是没有数据类型约束事件多了以后极难排查是谁丢了事件、谁多监听了一次。状态管理的数据流清晰调试也好查。具体做法是这样的在 store 里维护一个activeCategory字段左侧柱状图点击时 commit 这个字段右侧折线图组件 watch 对应字段变化后拉数据并 setOption。左侧组件和右侧组件完全解耦中间只通过 store 通信。代码结构大概是// store export const useDashboardStore defineStore(dashboard, { state: () ({ activeCategory: 全部 }), actions: { setActiveCategory(category) { this.activeCategory category } } })右侧组件 watchconst dashboardStore useDashboardStore() watch(() dashboardStore.activeCategory, (newCategory) { fetchData(newCategory).then((data) { option.value buildOption(data) }) })这套方案在 element-plus 搭建的中后台系统里非常稳新加图表只要订阅 store 对应字段就行根本不用改旧代码。2.3 让图表动起来动画、定时刷新与数据驱动更新ECharts 的动画参数经常被忽略其实它直接影响交互体感。animationDuration默认是 1000ms数据量小的时候还好数据量大时每轮刷新都像在放慢动作。我一般设animationDuration: 300配合animationEasing: cubicOut观感比较干脆。柱状图想要“逐个蹦出来”效果可以给数据项单独设置animationDelayanimationDelay: (index) index * 80数据驱动更新是交互组件性能的关键。很多人在 setOption 时直接把整个 option 对象替换掉这样图表所有状态都重置动画也会从零播放。其实 setOption 的第二个参数有两个布尔位notMerge和lazyUpdate。默认情况下 notMerge 是 false意味着新旧配置会自动合并但如果你传入了新的 series 数组但没有传原 series 里的自定义属性这些属性就会被覆盖掉。我的习惯是只更新数据时每次生成一个新的 objects 但保留通用配置结构只有真正需要清空状态时才用{ notMerge: true }。定时刷新的场景比如监控大屏每 5 秒拉一次数据一定要记得在组件卸载时清除定时器。我见过线上事故是定时器没清页面切走了还在后台拉接口把后端打爆了。除了清定时器还要做好请求竞态处理上一次请求还没回来下一次又发出去了数据返回顺序不对图表数据就会跳来跳去。简单做法是用一个requestId计数器每次请求时递增响应回来时判断requestId是否还是当前的不是就丢弃。3. 地图与3D从中国地图到 echarts-gl 的进阶玩法3.1 中国地图的正确打开方式geo、map 和 visualMap地图是 ECharts 交互组件里最容易“看起来会了一写就炸”的功能。首先要弄清楚两个概念geo是一个坐标系组件它可以被散点图scatter、效果散点图effectScatter等系列引用map是一个系列类型直接绘制地图区域自身。如果只是想画一张填色地图用series-map配合visualMap就够了如果想在地图上叠加散点、飞行线就要考虑用geo组件承载散点系列。无论哪种用法都需要先注册地图数据。中国地图的 GeoJSON 在过去是 ECharts 内置的新版已经拆出来按需加载需要单独引入并调用echarts.registerMap(china, chinaJson)。这里有个容易踩坑的点某些网络上下载的 GeoJSON 坐标不准确或者属性字段不规范注册后地图显示歪七扭八。我一般建议直接用官方维护的地图 JSON 文件或者在项目里固定一个地图 JSON 版本不要每次去网上随便搜“中国地图 json 下载”。注册完之后基础配置长这样option { geo: { map: china, roam: true, itemStyle: { areaColor: #e0e0e0 } }, series: [ { type: map, map: china, geoIndex: 0, data: [ { name: 北京, value: 100 }, { name: 上海, value: 200 } ] } ], visualMap: { min: 0, max: 200, inRange: { color: [#e0f3f8, #abd9e9, #74add1, #4575b4, #313695] } } }注意geoIndex是强调过的关联方式map系列不直接绘制地图而是复用前面geo组件的坐标系。这样做的好处是地图区域和业务数据的视觉映射可以分开控制交互也统一。3.2 visualMap pieces 与“9段变10段”的坑很多运营类需求喜欢把地图颜色做成“9 段图”或“10 段图”也就是把数值区间切成若干段每段一种颜色。默认情况下visualMap是连续型min/max配合splitNumber自动分段但这样分出来往往是等宽的 5 段没法满足业务自定义区间范围。这时候用visualMap的pieces配置最合适。pieces 就是手动指定每一段的范围、颜色、标签写起来非常直观visualMap: { type: piecewise, pieces: [ { min: 0, max: 100, label: 0-100, color: #fff7ec }, { min: 101, max: 500, label: 101-500, color: #fee8c8 }, { min: 501, max: 1000, label: 501-1000, color: #fdd49e }, { min: 1001, max: 5000, label: 1001-5000, color: #fdbb84 }, { min: 5001, max: 10000, label: 5001-10000, color: #fc8d59 }, { min: 10001, label: 10000以上, color: #d7301f } ] }热搜词里有个“echarts地图9段图变10段图”其实本质就是这个数据范围没变只是业务想从 9 个区间变成 10 个区间。用 pieces 改起来非常方便直接在数组里加或拆一个区间就行。但这里有两个隐含坑第一区间边界问题min和max都包含自身所以要确保边界不重叠第二如果业务数据里有负值或大异常值记得在 pieces 里覆盖到所有可能范围否则数据落不进任何一片区域时不会显示颜色。还有一点经验如果想让颜色渐变平滑就不要用 piecewise继续用连续型visualMap配合inRange.color数组。分段式图例适合“区间意义明确”的业务表达比如销售等级、风险等级连续渐变则适合表达“趋势强弱”比如迁徙热度、访问密度。3.3 给某个市“标数量”markPoint 和 label 都能干给地图上的某些城市标记数量这个需求遇到得很频繁。先说最简单的情况如果只是想让每个省份或城市显示数值不需要额外的标注点直接在series-map里配置label.show: true并用formatter或data.value显示数字即可。但这样会有一个问题当地图缩放或漫游时label 会跟着移动视觉上容易显得杂乱。更可控的方式是用markPoint或叠加一个scatter系列。markPoint可以配置在 map 系列之上也可以用scatter系列配合coordinateSystem: geo。两者区别是 scatter 系列可以充分利用visualMap做数值映射让点的大小、颜色随数量变化更适合“数量差异悬殊”的场景。下面是一个叠加散点的示例series: [ { type: effectScatter, coordinateSystem: geo, data: [ { name: 杭州, value: [120.15, 30.28, 2000] }, { name: 广州, value: [113.23, 23.16, 3500] } ], symbolSize: (val) Math.sqrt(val[2]) / 2, encode: { value: 2 } } ]这里必须要写经纬度写城市名称是不行的。如果手头只有城市名就需要有一份“城市名到经纬度”的映射表。另外大数据量时 effectScatter 的动画数量太多会卡建议用来标注重点城市即可不要每个地级市都用。3.4 长方形全球地图与 echarts-gl 3D 场景热搜里有“echarts 地图组件长方形展示 全球地图”这个场景通常出现在大屏项目的世界地图模块。ECharts 默认的全球地图是接近正方形的比例但大屏背景是宽屏直接塞进去两边会留很多空白。解决办法是调整geo的layoutCenter和layoutSize把地图固定到指定位置和大小如果是 map 系列可以通过mapLocation或者geo的aspectScale控制宽高比。实际操作中把layoutCenter: [50%, 50%]、layoutSize: 150%再配合aspectScale: 0.85左右地图比例就明显变长条铺满宽屏不被拉伸变形。3D 地图则是另一套体系需要引入 echarts-gl。热搜里的“echarts echarts-gl - 使用geo3d map3d scatter3d做3d地图”我复现过多次核心步骤是先注册地图 JSON再配置map3D组件import echarts-gl option { geo3D: { map: china, roam: true, itemStyle: { color: #1a3a5c, opacity: 1, borderWidth: 1, borderColor: #5bc0eb }, label: { show: false }, emphasis: { label: { show: true, color: #fff } }, shading: lambert, light: { main: { intensity: 1.2, shadow: true } } }, series: [ { type: scatter3D, coordinateSystem: geo3D, data: [/* { name: 杭州, value: [120.15, 30.28, 2000] } */], symbolSize: (val) Math.sqrt(val[2]), itemStyle: { color: #ffd15c } } ] }3D 地图做出来确实炫但真实现过一遍的人会知道坑很多。第一echarts-gl 对低端设备不友好复杂 geo3D 模型在大屏老安卓设备上卡成幻灯片建议只保留少量视觉元素不要同时开阴影、环境光、后期特效第二geo3D的标签默认是关闭的很多新手以为代码没生效其实是没开label.show第三3D 地图的经纬度高度值第三个维度通常表示数值大小需要注意数据归一化。纹理贴图在 3D 场景里也能用比如给地图底面贴一张科技感纹理或者给柱体贴材质贴图。echarts-gl 的material配置支持纹理属性但图片文件分辨率不要太大否则纹理上传 GPU 会很有压力。纹理贴图这个关键词在普通图表里也常见于柱状图用图片填充柱身让图表更有品牌感。4. 特殊 Chart 与真实业务场景盘点4.1 多 y 轴、甘特图、箱线图、树图这些“不常见图”怎么做先讲多 y 轴。ECharts 原生支持多轴配置只要把yAxis改成数组每个系列用yAxisIndex指定挂在哪个轴上。这个功能本身不难难的是怎么让两条轴的刻度不打架。如果两条轴的数值范围差特别大比如一个系列是 0-100另一个是 0-100000默认情况下两个轴各自独立缩放入会导致零线不对齐看起来很难受。处理方案一是给其中一个轴手动指定max和min让两个轴的零线对齐方案二是给每条轴设置scale: true让轴不从 0 开始从数据最小值附近开始。实际项目里我更常用第二种视觉上更紧凑。甘特图在 ECharts 里没有现成类型但可以通过custom系列或者堆叠条形图绕过去。我用得比较多的是 custom 系列因为可以精确控制每个任务条的位置、颜色、圆角还能加进度条效果。核心思路是每个任务用一个矩形元素xAxis放时间yAxis放任务名矩形的位置由任务的开始时间和时长换算成坐标值。项目排期、产线排班这类需求都可以用这种方式。箱线图在 element-plus 项目里配合 ECharts 的 boxplot 系列非常方便。它的原始数据格式是[min, Q1, median, Q3, max]多个箱子就是 series data 里多放几个数组或者用dataset来管理。有个容易忽略的坑如果直接用原始业务数据ECharts 不会自动替你算分位数需要先用工具函数计算好五个统计值再传入。我在项目里封装过一个computeBoxplot(data)函数输入二维数组输出每个组 [min, Q1, median, Q3, max] 的列表这样业务侧传原始数组就行组件内部做统计处理。树图的交互点在于展开收起。ECharts 树图默认会画所有层级节点多了以后一屏根本展示不开。可以配置initialTreeDepth: 1让初始只展示第一层用户点击节点再逐层展开。同时开启roam: true允许缩放和平移体验会好很多。4.2 纹理贴图与柱形异形图让普通图表有质感“柱形异形图”这个关键词出现不少。常规柱状图就是矩形柱子但有些大屏项目想要“人形柱”“水滴柱”“箭头柱”这种视觉表达。实现方式有几种一种是给series-bar配置圆角itemStyle.borderRadius这是最简单的异形再进阶一点用scatter系列 symbol把散点的 symbol 设置成image:// url或者path://路径就能画出任何形状。纹理贴图同样是提升质感的手段。普通柱状图可以给itemStyle.color配置一个图片对象itemStyle: { color: { image: textureImg, repeat: repeat } }这样柱身会平铺图片纹理比纯色好看不少。在 echarts-gl 里纹理贴图更多用在 3D 柱体和地面上。有一个经验纹理图片尺寸不用太大重复平铺时一张 128x128 的图片和 1024x1024 的图片在视觉效果上差别不大但显存占用差很多大屏项目里尽量控制。4.3 uniapp、ThingsBoard、低代码编辑ECharts 在不同容器里的生存指南uniapp 使用 ECharts 是另一个常见话题。在 App 端或 H5 端可以直接使用 renderjs 方案在小程序端则要用 ec-canvas 或其他适配库。核心难点是 canvas 类型不同小程序新版 canvas 2d 接口需要传 node 实例。我踩过的坑是onReady 时机太早canvas 还没渲染完成就 init图表一片空白。解决方法是等 canvas 节点完全创建后再初始化必要时加一个重试机制。ThingsBoard 里的 ECharts widget 是专门的集成场景。ThingsBoard 封装了自定义 widget 的 API核心是通过self.ctx获取数据订阅和设置方法然后在ctx.$container里创建图表容器在数据回调里更新 option。它与普通网页开发的最大区别是生命周期由 ThingsBoard 管理不能想当然地在 onMounted 里 init要遵循它的 widget 生命周期函数。低代码可编辑图表这两年很火。我参与过一个内部报表平台做法是将 ECharts 的 option 抽象成 JSON Schema编辑器中拖拽字段自动生成 option渲染器统一用一个 ChartRenderer 组件接收完整 option 直接 setOption。这样做的好处是图表配置可持久化、可版本管理、可跨用户复用。难点是 option 里有很多函数比如 formatter存 JSON 时函数会被丢掉。我们的方案是用字符串模板解析在 setOption 之前把字符串转成 Function或者尽量避免在 JSON 里写函数改成组件内置预设。5. 常见报错与排查实录5.1 “raw”报错和数据初始化顺序热搜词里有个“vue echarts图异常 raw”我估计遇到的人不少。这个 raw 报错通常有几种来源一是setOption时传了 undefined 或 null二是 echarts 实例在 DOM 还没渲染完成时初始化拿到的容器尺寸是 0三是某些系列的字段类型不对比如data里的值出现了NaN或字符串与数字混用。排查思路很简单捕获异常后打印当前 option重点检查 series.data 里有没有 NaN、undefined容器有没有高度。遇到 raw 报错时我还会检查是否有两个版本 ECharts 同时存在某些依赖比如 thingsboard 自带 echarts 版本与项目全局版本冲突也会出现奇奇怪怪的报错。5.2 地图不显示、tooltip 不出现、多 y 轴刻度重叠下面这张表是我在生产环境遇到最多的问题现象可能原因处理方式地图区域空白地图 JSON 未注册或注册名与配置不一致检查 registerMap 调用确认 map 名称地图显示了但没数据data 里的 name 与 GeoJSON 属性名不匹配打印 GeoJSON 的 properties对齐名称tooltip 被容器边界截断未设置 confinetooltip 配置加confine: truetooltip 显示空白没有配置 formatter且 value 结构复杂自定义 formatter返回清晰字符串多 y 轴刻度重叠两个轴的 max/min 范围差异过大手动指定 max/min或开启 scale图表初始化后不显示容器高度为 0 或 display:none确保容器有明确高度并可见后 init地图不显示是最容易慌的但其实十有八九是名称不匹配。比如 GeoJSON 里城市名是“北京市”如果你的 data 里写的是“北京”图表会直接忽略这条数据不报错、不提示。排查办法是把 GeoJSON 的 name 字段全部抽出来打印一次对照修改业务数据。多 y 轴刻度重叠的另一种情况是左右两轴的标签文本太长都挤在同一个位置。除了调整轴的范围还可以设置axisLabel.rotate或者缩短标签文本保证两条轴的刻度标签物理位置错开。5.3 性能与内存泄漏大屏项目最容易翻车的地方做数据大屏最容易忽视的就是 ECharts 性能问题。一次渲染几十张图表全部使用默认配置一旦数据刷新所有图表同时 setOption页面直接卡死。这里有几个关键优化点第一按需引入。不要把整个 echarts 包都打进 bundle而是用echarts/core配合use按需注册需要的图表和组件。代码就算不改打包体积也能从 1MB 降到 400KB 左右。第二合理选择渲染器。默认是 canvas适合数据量大的场景。SVG 在图表数量多、但每个图表数据量小的情况下表现更好因为 DOM 操作比 canvas 重绘更灵活。小屏展示和移动端我更倾向 SVG。第三数据采样。ECharts 内置了sampling: lttb时间序列数据量超过千个点后开启视觉上几乎看不出差别性能提升非常可观。再配合progressive渐进渲染几千个点也能顺畅刷新。第四内存泄漏。最常见的是组件销毁时没有dispose其次是 ResizeObserver 没有 disconnect再有是定时器没有清理。在 SPA 应用里路由切换后图表实例还持有 DOM 引用内存回收不掉时间长了页面越来越卡。我封装 BaseChart 时把 dispose 写在onBeforeUnmount里就是为了强制处理这个问题。5.4 独门调试技巧比 console.log 好用十倍的方式最后分享几个调试技巧。首先拿到图表实例后不一定要依赖业务代码可以在控制台手动执行const chart echarts.getInstanceByDom(document.getElementById(chart)) chart.getOption() chart.setOption({ /* 临时改配置 */ })getInstanceByDom是调试神器它能在不重新初始化的情况下直接拿到当前图表实例改完配置立刻看到效果。定位问题比一遍遍刷新页面高效得多。其次给 option 加上数据层和配置层的分离。我习惯把静态配置坐标轴、颜色、图例样式和动态数据series.data拆成两个对象setOption 时合并。这样调试时可以很清楚地看到是哪个部分导致显示异常也不会因为两周前改了一处样式波及到数据。还有一个经验在开发环境写一个全局的window.__charts数组每次 init 图表时 push 进去排查多个图表实例之间的状态时非常方便。生产环境记得删掉属于开发辅助代码留在线上有泄漏风险。另外如果数据请求失败或返回空数组不要直接 setOption否则图表会显示空白并可能抛异常。我在组件层做了一个空数据兜底当 series.data 为空时显示一个自定义的“暂无数据”覆盖层并在控制台 warning而不是让用户面对一张空白画布。这个小细节在业务系统里非常加分。做 ECharts 交互组件这些年我最大的体会是图表的难点从来不在画而在“数据怎么流转、状态怎么管理、生命周期怎么控制”。把 init、setOption、resize、事件绑定、dispose 这几件事想清楚任何图表需求都不难。我现在的习惯是拿到一个可视化需求后先画数据流转图再拆组件和状态最后才动手写 option 配置。顺序反了后续维护就是无底洞。最后再分享一个小技巧option 里把通用配置抽到顶层常量业务数据单独维护交互状态用 store 管理这三层分开项目结构就稳了一大半。
返回列表