项目:html5-dataviz | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
本章逐条登记源码中可辨识的技术债,每条按"位置 → 证据 → 影响 → 建议"四段展开。项目的特殊性在于它是一个高频治理期的资产型代码库(00 篇第 7 节的演进表记录了 2026-10 密集的修复与收敛),因此这里的债务多数不是"欠了很久的烂账",而是"有意识保留的取舍"——登记它们是为了让取舍可见、可复议,而不是宣判。优先级总览:
| # | 债务项 | 等级 | 类型 |
|---|---|---|---|
| 1 | manifest 生成器缺失与覆盖风险 | 高 | 工具链断裂 |
| 2 | CODE_TO_VCHART_GUIDE 提及的 echarts-setup.ts 已不存在 | 中 | 文档漂移 |
| 3 | sourceUrl / sortOrder 写而不读 | 低 | 死字段 |
| 4 | coverage 无独立 script 与阈值 | 中 | 质量门禁缺口 |
| 5 | Google Fonts 未本地化 | 中 | 内网部署缺口 |
| 6 | EChartDemo 全量 echarts 静态依赖 | 中 | 体积取舍 |
| 7 | tsconfig 排除 chart-examples/three | 中 | 类型盲区 |
| 8 | 大屏迁移存量 lint 豁免 | 低 | 治理未尽 |
| 9 | loadScript 无来源校验等安全护栏未合拢 | 低 | 见 08 篇 |
| 10 | 冒烟未纳入 CI | 高 | 质量门禁缺口 |
位置:src/chart-examples/manifest.ts(3812 行)头部注释;scripts/ 目录无对应脚本。
证据:文件第 1~2 行明确写着"本文件由 server/scripts/gen-charts-local.ts 自动生成,请勿手改。重新生成:在 server 目录运行 node_modules/.bin/tsx scripts/gen-charts-local.ts",但该 server 仓库未随项目提供,全仓库检索不到 gen-charts-local 的任何副本;同仓库的 three 侧则有完整生成器 scripts/gen-three-registry.mjs,两相对照证明"清单必须有生成器"是本项目自己确立的工程标准。当前 echarts/antv 清单的任何变更(新增案例、改 title/tags)只能手工编辑这个"勿手改"文件,《06-部署运行》第 5 节的 SOP 也是在这个前提下写的("手工追加条目是当前仓库内的实际可行做法")。
影响:其一,覆盖风险——一旦找回生成器重跑,手工追加的条目会被整体覆盖(生成器按上游源重算,不知道仓库里有过手改),git 历史是唯一恢复依据;其二,一致性风险——手工编辑 3812 行文件容易破坏按 id 排序、字段形态等隐含约定,错误会传导进守恒测试与详情页;其三,可维护性——manifest 与 JSON 的投影关系(04 篇第 2 节)全靠 charts-data.test.ts 对账,没有生成器保证"写时一致"。
建议:三选一。优先方案是把生成逻辑收编进本仓库 scripts/(参照 gen-three-registry.mjs 的形态,输入改为官方示例元数据快照);次选是找回 server 仓库并约定"再生成前 diff 手工条目";最低成本方案是在 manifest.ts 头注释中如实改写现状(标注"生成器缺失,手工维护,追加后 git 留痕"),避免后来者按注释去找一个不存在的脚本。
位置:docs/CODE_TO_VCHART_GUIDE.md 第 10 行。
证据:该行写明"<v-chart> 已全局注册,echarts-setup.ts 已在 ChartExampleDetailPage 引入,echarts 可用";但全仓库检索 echarts-setup 仅此一处,src/chart-examples/echarts-setup.ts 文件不存在,ChartExampleDetailPage.vue 中也没有任何对应 import(其对 echarts 的依赖经由 EChartDemo 的静态 import 间接成立)。结合 EChartDemo 头注释"2026-10 重设计……曾有的 optionCode/code/gl 字符串执行路径已随迁移退役"可判断:该手册写于迁移早期,描述的引导机制(独立 setup 文件提前加载 echarts)在 EChartDemo 重设计时被"模块顶部静态 import"替代,手册未同步。
影响:这是权威规范文档(00 篇第 2.5 节称其为"理解 echarts 本地 SFC 为什么呈现统一形态的权威依据")中的事实性错误,会误导新维护者去找一个不存在的文件、按不存在的机制理解加载时序;同时手册的模板规范(<div class="h-full w-full"><v-chart ...>)与现行 294 个 SFC 的实际形态(demo-container + EChartDemo 容器)已不一致,按手册新写的示例会破坏 EChartDemo 契约。
建议:以 EChartDemo.vue 头注释与现行 SFC 实际形态为准修订该手册:删除 echarts-setup 表述、模板规范改为 demo-container + EChartDemo 三模式、标注"option/onReady/setup 契约只能收紧不能变更"。修订成本极低(半小时级),但消除的是新成员第一周就会踩的认知陷阱,属高性价比债务。
位置:src/types/chart-examples.ts(字段声明)、src/data/charts/*.json(数据)、src/data/charts/__tests__/charts-data.test.ts(REQUIRED_KEYS)。
证据:运行时消费面检索显示,除类型定义与测试的必需键清单外,全站无任何组件/函数读取这两个字段。数据侧的实际填充率(实测统计):sourceUrl 在 echarts.json 348 条全部非空(指向 echarts.apache.org 官方示例编辑器链接)、antv.json 346/351 非空(指向 antvis/g2plot GitHub 目录)、three.json 全空;sortOrder 在 antv 351 条全非零、echarts 347/348 非零(0,1,2,3… 递增序列)、three 984/989 非零。可见 04 篇第 1 节"sourceUrl 多为 null"的表述仅对 three 成立,echarts/antv 是"数据齐全但无处展示"。
影响:影响为正负两面。负面:约 3.2MB JSON(echarts.json 1.22MB 的大头是 optionCode 之外的字段与 URL 字符串)携带运行时不消费的信息,列表页首次加载白白传输;sortOrder 的"排序权重"语义从未生效(《04-数据模型》第 1 节已标注其实际影响待确认,实测列表排序按 JSON 数组原序)。正面:sourceUrl 是唯一的官方溯源通道,未来做"查看官方原版"功能时数据现成。
建议:不建议直接删字段(删了要同步 19 字段 schema、REQUIRED_KEYS 与生成器对齐,收益只是几百 KB)。合理动作是二选一:短期在详情页元信息区把 sourceUrl 渲染为"官方示例"外链(数据已备、一行模板代码的成本);或长期在 slim-chart-json.mjs 中把三个全量本地 SFC 库的 sourceUrl/sortOrder 也纳入瘦身(前提是确认永不展示)。
位置:package.json scripts;vitest.config.ts。
证据:@vitest/coverage-v8 已安装(^2.1.9),但 scripts 中没有 test:coverage 之类的条目(只能 pnpm vitest run --coverage 间接触发),vitest.config.ts 的 test 配置块也没有 coverage 段(无 include/exclude/thresholds),CI 四步中亦无覆盖率环节。
影响:覆盖率数据随时可产但没有基线记录,无法回答"渲染链工具层的真实覆盖是多少"这类问题;没有 thresholds 意味着测试规模只能靠 review 约束不回退(09 篇第 8 节的补测建议缺一个客观标尺)。
建议:第一步加 script("test:coverage": "vitest run --coverage")并跑一次留档基线;第二步在 vitest.config.ts 配置 coverage.include 限定 src/utils/**、src/composables/**、src/api/**、src/components/chart-examples/** 四个高价值区,设保守阈值(如 60%)防回退;不建议对 src/chart-examples/**(生成资产)与 22 个大屏设阈值,覆盖它们不是单元测试的职责。
位置:index.html 头部三族字体(Space Grotesk / Inter / JetBrains Mono)的 preconnect 与 stylesheet 链接。
证据:这是全站唯一的功能性边界外网络依赖(smoke 的 EXTERNAL_HOST 正则刻意不含 googleapis.com,语义见《08-安全与权限设计》第 6 节);仓库 public/ 下无对应字体资产,pnpm build 产物 dist/index.html 原样保留外链。
影响:纯内网(物理断网)环境下三次 preconnect 全部失败,字体回落系统族——功能无损但视觉降级,且 DevTools 每页有 3 条失败请求噪音;公网环境则存在加载时序抖动(display=swap 已缓解阻塞)。README 部署一节的"运行时零外链"承诺在字体这一项上是打了折扣的。
建议:用 fontsource 方案本地化——@fontsource/space-grotesk、@fontsource/inter、@fontsource/jetbrains-mono 三个 npm 包替代 CDN 链接,在 src/main.ts 引入对应 weight 的 css;或直接下载 woff2 放 public/fonts/ 并手写 @font-face。改完 smoke 的 PAGES 断言自动覆盖(外链正则可顺手加上 googleapis 域名,把字体也纳入零外链断言)。
位置:src/chart-examples/EChartDemo.vue 第 16 行 import * as echarts from 'echarts'。
证据:注释自述动机"全量 echarts 已在模块顶部静态引入(painter 注册时序确定)";体积后果实测为 echarts 核心 chunk 1.19MB + echarts-vendor 1.18MB,任一 echarts 示例详情页必载(见 10 篇第 2.3 节)。
影响:294 个 SFC 的公共成本,其中大量简单示例(如 line-simple)只用到基础坐标系系列,全量引入约 3 倍于其实际需要。
建议:见 10 篇第 8 节建议 1(echarts/core + 按需注册),迁移窗口选在无功能发版期,用 sweep-fallback.mjs 全量清扫护航;此项与 #2 的手册修订同批做,因为改写规范会随之变化。
位置:tsconfig.json 的 exclude 数组。
证据:"exclude": ["node_modules", "dist", "src/chart-examples/three"]——989 个 three SFC 及其 stage/shared 不参与 vue-tsc 类型检查;同时 eslint.config.js 也整体豁免了 src/chart-examples/**。
影响:three 侧的类型错误(如 ExampleScene 壳族与示例之间的 props 契约破坏)在 typecheck 闸门完全不可见,只能靠 three-data.test.ts(元数据层)与运行时暴露;修改 src/chart-examples/three/stage/ 下的共享代码时没有类型安全网。
建议:不急于把 989 个生成文件收回检查(成本高、噪音大),可折中:exclude 保持,但在 tsconfig 新增一个 references 子项目仅覆盖 src/chart-examples/three/stage/** 与壳组件 *.vue(源码级共享件),生成示例仍排除——共享件是唯一"人写且被 989 个文件依赖"的区域,值得类型保护。
位置:eslint.config.js ignores 列表。
证据:除 src/chart-examples/** 外,另有 20 个 src/views/bigscreen/** 子目录被逐条豁免,注释标注"遗留大屏实现(迁移存量,逐步治理中);business/report/dashboard 三个业务大屏与根层页面/测试保持 lint 覆盖"。这本身就是一份治理清单——豁免的存在说明这些目录的代码质量未达基线(no-explicit-any 是 error 级),且"逐步治理中"的进度无 tracking。
影响:新大屏若模仿存量风格会要求同样豁免,豁免面扩大;存量目录中的真实 bug(未使用变量掩盖的拼写错误等)逃过静态检查。
建议:把 20 个豁免目录按"继续保留(拟重写)/ 治理后收编 / 直接收编"三档标注到 eslint.config.js 注释或独立 issue,每收编一个就删一条 ignore;禁止新增豁免条目(SOP-B 已有此约定)。
位置与证据:见《08-安全与权限设计》第 8 节六条隐患(loadScript 无来源校验、危险模式正则可绕过、script 注入通道绕过依赖闸门、Google Fonts 外链、new Function 对 CSP unsafe-eval 的依赖、DOMPurify 未工具化),此处不重复展开。
建议:优先级最高的是 #1 与 #5:给 loadScript 加本地路径断言是一行防御(if (!src.startsWith('/')) throw),收益是堵住唯一"约定级"防线;CSP 与 eval 的冲突需在内网部署方案定稿前确认,否则兜底链路整体失效。
位置:.github/workflows/ci.yml(lint/typecheck/test/build 四步);scripts/smoke.mjs。
证据:06 篇第 4.2 节已说明 smoke 需要起服务与浏览器,当前靠发布前人工执行;外链零请求断言、canvas 渲染证据、GL 兜底链路探针在 CI 中均为盲区。
影响:改依赖(升级 three/echarts、加减包)后的外链回归与渲染回归完全依赖开发者自觉跑 smoke——而 08 篇已论证这恰是外链清零最易被破坏的场景。
建议:CI 增加 smoke job:pnpm build → pnpm preview(后台)→ node scripts/smoke.mjs。Playwright 已在 devDependencies,主要成本是 CI 安装 Chromium(actions 支持 playwright 官方 action 或 npx playwright install --with-deps);若全量跑过重,可拆"PR 跑 4 个探针页 + 发布前全量"两档。
综合十条债务与各章建议,按三个阶段给出路线图(阶段划分以"不改架构"为边界):
第一阶段:零风险收尾(1~2 人日)——修订 CODE_TO_VCHART_GUIDE.md(#2);package.json 加 coverage script 并留档基线(#4 第一步);loadScript 加路径断言(#9);manifest.ts 头注释改写现状声明(#1 最低成本版);详情页渲染 sourceUrl 外链(#3 短期版)。全部是单文件小改,无行为面变化。
第二阶段:门禁补全(1~2 人周)——CI 接入 smoke(#10);coverage 阈值与范围配置(#4 第二步);Google Fonts 本地化并扩充外链断言(#5);补 sfcRuntime 安全校验、loadScript、三 composable 的单元测试(09 篇第 8 节清单 2~4);eslint 豁免三档标注(#8)。
第三阶段:结构性优化(按需立项)——echarts 按需注册改造(#6,需 sweep 护航);manifest 生成器收编进本仓库(#1 优先方案);three stage 共享件的类型子项目(#7);three-vendor/字体子集化等体积专项(10 篇第 8 节);若未来要承载用户提交的 SFC,则按 08 篇第 2.3 节引入 Worker + CSP 真隔离——那是架构级变更,应独立立项评审,而非在本路线图内顺带完成。
路线图的排序原则:先堵"会悄悄坏"的(门禁),再还"欠着但可见"的(文档与死字段),最后动"收益大但需护航"的(体积与架构)。每阶段完成后,本章表格的等级列应同步更新——技术债登记的价值不在列表本身,而在它随治理进程被逐条划掉。