项目:html5-dataviz | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
本项目的扩展成本几乎全部落在"注册一致性"上,而不是代码本身。《03-接口设计》第 5 节列出了契约与测试的对应关系,动手前先记住三条铁律:其一,id 是唯一身份,JSON 条目、SFC 文件名、manifest 条目、缩略图文件名、编译缓存键全部与它联动,任何一处错位都会被 charts-data.test.ts 或详情页 404 暴露;其二,新增必有 JSON 条目,列表页数据完全来自 src/data/charts/*.json,只有 SFC 没有 JSON 条目的案例在列表里不存在;其三,改完跑守恒测试,pnpm test 中数据一致性用例是最快的事后发现层。以下六个场景按使用频率排序。
标准流程六步已在《06-部署运行》第 5 节给出(创建 SFC → 注册 manifest → 注册 JSON → 缩略图 → 更新守恒测试 → 本地验证),本节补充执行细节与易错点。
SFC 契约要点。echarts 示例的骨架是 <div class="demo-container"><EChartDemo :option="option" /></div> + import EChartDemo from '../../EChartDemo.vue'。三种执行模式的选用原则(契约细节见《07-模块详解》第 4 节):纯静态图用普通对象 option;需要异步取数/注册地图用 onReady(ctx)(返回 option 由 EChartDemo 代为 setOption);需要定时器/事件监听/原生实例 API 用 setup(ctx) 并必须返回清理函数(否则示例快速切换或重试时定时器叠加,这是历史事故类别)。GL 系列(globe/bar3D 等)在 SFC 里写 import 'echarts-gl' 且必须放在 import * as echarts from 'echarts' 之后;地图数据不要自行 fetch+registerMap 以外的路线,ctx.registerMap 可用,/map-geojson/ 下有 china/world 及 34 个省级文件可直接 fetch。antv 示例则是自驱模式:onMounted 里 new Plot(containerRef.value, options) + render(),onBeforeUnmount 里 plot.destroy(),不经过任何宿主组件。
分类目录与 manifest 条目。目录名建议与 JSON category 一致的拼音/英文形式(现有 src/chart-examples/echarts/ 下是 bar/line/map 等英文目录)。manifest 条目手工追加进 src/chart-examples/manifest.ts 对应数组(注意该文件头标注"自动生成勿手改",手工追加是当前唯一可行做法,原因与风险见 SOP-D):id 与文件名严格一致、category 尽量复用已有 37/51 个分类之一(新分类会出现在侧栏但破坏分类体系的一致性)、tags 参与搜索。JSON 条目的 19 字段 schema 见《04-数据模型》第 1 节;loaderKind 填 option 或 api-async(echarts)/antv-config(antv),有本地 SFC 就不要写代码字段(写了会被守恒测试第 4 用例拒绝),thumbnail 可置 null 走 /chart-thumbs/{id}.webp 兜底。
验证清单:pnpm test(守恒 + 模板表达式断言)→ pnpm dev 看列表卡片、详情 canvas、源码弹窗、下载按钮 → 可选把新案例 id 加进 scripts/smoke.mjs 的 PAGES 成为永久冒烟探针。
三个高频翻车点的预防性说明。第一是"缩略图先行"的误区:很多人按传统习惯先做图再写代码,但本项目的守恒测试不校验 echarts/antv 缩略图(只有 three 与大屏校验),所以缩略图缺失不会在测试期暴露、只会以列表 📊 占位符的形式静默降级——上线前应人工过一遍列表页而非只看测试绿。第二是 id 的隐蔽约束:id 会成为 URL 路径段(/charts/echarts/{id}),含中文或特殊字符的 id 虽然技术上可用(three 侧甚至有 URL 编码的中文文件名先例),但会让路由、缩略图名、smoke 探针全部变得脆弱,新案例一律用 kebab-case 英文。第三是分类目录与 JSON category 的关系:目录名(src/chart-examples/echarts/{dir}/)与 category 字段没有代码级耦合(详情页 findLocalModule 按文件名后缀全局匹配,不依赖目录),但保持两者对应是 gen-charts-local 生成器的隐含布局约定,破坏它会增加未来找回生成器时 diff 对账的成本。
大屏是"双真源注册表"体系(见《07-模块详解》第 6 节),步骤:
src/views/bigscreen/{name}/{Name}Page.vue,根节点用 DatavContainer(1920×1080 基准自动缩放),内部图表直接写 <v-chart>(全局已注册,无需 import),暗色主题消费 src/utils/echarts-theme.ts 的 darkEchartsTheme;src/config/bigscreenData.ts 追加 { id, title, description, category, thumbnail },category 从现有 12 类中选;src/config/bigscreenComponents.ts 追加同 id 的动态 import 加载器——两处 id 必须完全一致,src/config/__tests__/bigscreenComponents.test.ts 的双向对账用例会拦截任何漂移;public/bigscreen-thumbnails/{id}.webp,bigscreenThumbnails.test.ts 校验存在性;pnpm test 全绿后 pnpm dev 访问 /bigscreen/{id} 验证缩放与渲染;若有静态数据需求,优先组件内联或 public/report/ 式静态 JSON + Pinia 模式(参考 src/stores/reportStore.ts)。易错点:新大屏目录若属"迁移存量"风格需要 lint 豁免,需同步 eslint.config.js 的 ignores 列表(business/report/dashboard 三个目录是显式保持 lint 覆盖的反例,不建议新增豁免);meta.hideHeader 的全屏行为由详情页统一处理,大屏组件自身不要假设头部存在与否。
本项目曾在 2026-10 删除 apexcharts/chartjs/d3 三库(README 关键约定、src/api/chart-examples.ts 注释记录原因:"渲染器从未实现、数据残缺"),若要重新接入或新增第五个库,完整面涉及五处:
src/config/chartLibraries.ts 追加 { id, name, description, nav }——这是唯一真源,导航下拉与 /charts 路由校验自动联动(isValidLibrary 前置拦截非法库);src/data/charts/{lib}.json(schema 见 04 篇)+ src/api/chart-examples.ts 的 DATASET_LOADERS 追加动态 import 加载器(未知库返回空集的语义已就位);local,manifest + glob 直挂,Renderer 不感知),要么在 ChartExampleRenderer 的 renderExample switch 中新增分支(像 antv-config)——后者需要同步 src/components/chart-examples/__tests__/ChartExampleRenderer.test.ts;src/chart-examples/ 新建 {lib}-manifest.ts 并挂进 LOCAL_LIBRARY_MANIFESTS(详情页 tryLoadLocal 以它为入口);vite.config.ts 的 manualChunks 加桶规则,否则会被收进 vendor 桶拖累首屏;charts-data.test.ts 的 FILES 数组追加该库条目(含硬编码条数)。删除库的反向操作同理(历史先例的执行痕迹见 00 篇第 7 节演进表):删 chartLibraries 条目 + JSON + SFC 目录 + manifest,slim-chart-json.mjs 已内置"死库返回空集"的兼容(localSfcIds 对缺失目录返回空集)。
echarts/antv 的 manifest 与 JSON 原本由 server 侧 gen-charts-local.ts 生成(src/chart-examples/manifest.ts 头注释:"重新生成:在 server 目录运行 node_modules/.bin/tsx scripts/gen-charts-local.ts"),但该 server 仓库未随本项目提供——这意味着重新生成链路断裂,手工编辑是当前唯一维护方式。应对策略:
scripts/gen-three-registry.mjs 写一个一次性脚本——它演示了完整的生成器形态:按 ChartExample schema 生成 JSON、维护 manifest 数组、资产裁剪、四项一致性校验(id 跨库唯一/组件存在/缩略图存在/失败退出码 1);FILES 硬编码数字必须与新规模同步,这看似繁琐,实则是防止生成器半途而废留下脏数据的唯一闸门;three 库有仓库内生成器,规范路径是迁移而非手写:node scripts/gen-three-registry.mjs --source {source} --curio /path/to/curio。脚本自动完成组件拷贝(保持与 curio 字节一致,目录布局镜像源结构使相对导入原样生效)、three.json 生成(loaderKind 一律 local、无代码字段)、资产裁剪拷贝、一致性校验。手工新增(无 curio 源时)需同步四处:SFC(基于 src/chart-examples/three/ExampleScene.vue 壳族,含 CSS3D/SVG/WebGPU/WebXR 变体,@ready="onReady(ctx)" 中拿 ctx.scene 与 ctx.addUpdateCallback)、src/chart-examples/three-manifest.ts(含 globals.ASSET_HOST 若需资产根)、three.json、缩略图;资产放 public/ 对应根路径。不要绕过 ExampleScene 自建 renderer——context 上限防护(forceContextLoss)等安全网都在壳里,绕过即失去(见 10 篇第 4 节)。写完跑 three-data.test.ts,它锁 manifest 对齐(含 globals 逐字段)与缩略图存在性。
无本地 SFC 的兜底条目(现有 54 echarts + 17 antv)是另一条扩展路径——适合"只想收录官方脚本、不改写为 SFC"的场景:JSON 条目保留 optionCode(echarts 官方多语句脚本原样粘贴,结尾留 option = {...} 或自行 setOption)、loaderKind 填 option/api-async、按需置 requiresGl/requiresMap 布尔位(Renderer 据此预载扩展/预注册地图,见《07-模块详解》第 3 节)。若脚本依赖裸全局扩展库(ecStat/ecSimpleTransform/ecSimpleOptionPlayer 之外的新库),两步接入:文件放 public/echarts-data/lib/;ChartExampleRenderer.renderEchartsOption 中仿照现有三条 /ecStat/.test(code) 检测行追加一条 loadScript。component 类条目(SFC 字符串进 componentCode 走沙箱编译)原则上不再新增——那是编辑能力移除前的历史形态,且 usesSfc 的类型优先路由会豁免带 optionCode 的条目,新增前先确认真的不走 SFC/option 链路。无论哪类兜底条目,完成后必跑 scripts/sweep-fallback.mjs(全量渲染清扫,逐条实测 canvas/错误态,用法见脚本头注释),它是对"兜底条目渲染正确性"唯一成体系的验证手段。
任何扩展完成后的一致性收尾固定为四条命令:pnpm typecheck(vue-tsc,注意 tsconfig.json 排除了 src/chart-examples/three,three 侧改动靠测试兜底)→ pnpm lint(豁免矩阵见《14-配置与环境详解》)→ pnpm test(守恒 + 契约)→ 起服务后 pnpm test:smoke(若动了渲染链或依赖,外链零请求断言最容易在此暴露问题)。建议为每次扩展单独提交 git commit,并在 commit message 中标注涉及的 id 清单——在生成器缺失(SOP-D)的背景下,这些提交记录就是项目的事实资产账本。
六个 SOP 覆盖的场景之间存在选型交叉,最后的决策依据值得单独说明。**"本地 SFC 还是兜底条目"是案例扩展的第一道岔口:本地 SFC 是架构推荐路径(类型安全、源码即真源、渲染快),适合任何打算长期维护的案例;兜底条目(optionCode 原样收录)只在"忠实保留官方脚本原貌"或"批量收录、来不及改写"时使用,且批量收录后应以 sweep-fallback.mjs 清扫结果决定哪些值得升格为 SFC——这正是历史路径:echarts 348 条中已有 294 条完成 SFC 化,剩余 54 条兜底是迁移长尾而非目标形态。"生成器还是手工"是第二道岔口:有仓库内生成器的(three)一律走生成器,没有的(echarts/antv)批量场景建议先写一次性生成脚本再动手,单条场景手工即可。"改注册表还是改渲染器"**是第三道岔口:新增案例永远只动注册表(JSON/manifest/映射),任何"顺手改一下渲染器让它兼容"的冲动都应被拒绝——渲染器的每次松动都会稀释《08-安全与权限设计》的护栏与《09-测试与质量保障》的契约锁定。如果确需新渲染行为,按 SOP-C 走完整适配器流程,让新链路有自己的分派字段、测试与冒烟探针,而不是寄生在旧链路里。