项目:html5-dataviz | 版本:v1.0 | 生成日期:2026-10-05 | 依据:源码分析
| 项 | 要求 | 依据 |
|---|---|---|
| Node.js | 22.x | .github/workflows/ci.yml 的 setup-node@v4 node-version: 22 |
| 包管理器 | pnpm 10 | CI 的 pnpm/action-setup@v4 version: 10;仓库有 pnpm-lock.yaml |
| 内存 | 构建期 ≥8GB 可用 | package.json 的 build 脚本带 NODE_OPTIONS='--max-old-space-size=8192'(1617 个 SFC 的 glob 切分吃内存;另有免大内存的 build:fast 备选) |
| 浏览器 | 支持 ESNext 的现代浏览器 | vite.config.ts 的 build.target: 'esnext' 与 optimizeDeps.esbuildOptions.target: 'esnext'(three 官方包含顶层 await,注释明确说明) |
| 测试 | Playwright Chromium(冒烟) | devDependencies.playwright ^1.61.0;scripts/smoke.mjs 用 chromium.launch() |
cd html5-dataviz
pnpm install # 1. 安装依赖(frozen-lockfile 由 CI 使用)
pnpm dev # 2. 启动开发服务器 → http://localhost:5566
# ("dev" 脚本 = vite --port 5566,端口在 package.json 写死)
pnpm typecheck # 3.(可选)vue-tsc --noEmit,CI 阻塞项
pnpm lint # 4.(可选)ESLint
pnpm test # 5.(可选)Vitest 单元测试(run 模式)
pnpm test:watch # 或监视模式开发注意事项:
optimizeDeps target 已调至 esnext,若自行改动依赖出现顶层 await 报错,检查是否绕开了该配置。unplugin-auto-import 与 unplugin-vue-components(vite.config.ts)会在 dev/build 时改写 auto-imports.d.ts 与 components.d.ts——这两个文件是生成物,勿手改。components.d.ts 中有全局类型声明,SFC 内可不 import 直接使用;但源码中普遍仍显式 import(可读性优先)。ElementPlusResolver 自动引入,但按钮/输入框的样式在 src/main.ts 显式引入了两条 CSS(注释说明了原因)。pnpm build # NODE_OPTIONS='--max-old-space-size=8192' vite build → dist/
pnpm preview # 本地预览构建产物产物特征(由 vite.config.ts 决定):
echarts-gl-vendor、echarts-vendor(echarts + vue-echarts + @echarts-x)、antv-vendor(@antv/*)、three-vendor(three 及生态)、vendor(vue/vue-router/pinia/@vueuse/element-plus/dayjs 框架运行时桶)。manualChunks 只归组 node_modules,src 代码(含按库懒加载的 JSON)不进 vendor 桶——注释记录了"Vue 运行时曾被并进 echarts-vendor、入口 modulepreload 了 9.3MB echarts"的修复史。import.meta.glob)、三个库 JSON(动态 import)、22 个大屏(bigscreenComponentMap 懒加载器)各自异步分片。public/threejs-official 419M、three-project 192M、external-examples 180M)与 71MB echarts-data,README 部署一节明确"将 dist/ 交由任意静态服务器托管(无后端依赖)"。部署要点:
src/router/index.ts 使用 createWebHistory(),静态服务器必须把所有未命中路径回退到 index.html(SPA fallback),否则刷新非根路径会 404。index.html 仅有的 fonts.googleapis.com 字体外链属于"渐进增强"(加载失败回落系统字体,不阻塞功能);smoke 的外链断言正则(jsdelivr.net|aliyun.com|youbao.xyz|alipayobjects.com)必须保持通过。部署到内网时可考虑把字体一并本地化(⚠️ 待确认:仓库内未见到字体本地化资产)。scripts/sweep-fallback.mjs / sweep-three.mjs 类治理脚本核对引用关系。.github/workflows/ci.yml,push main / PR 触发):pnpm install --frozen-lockfile → pnpm lint → pnpm typecheck → pnpm test → pnpm build,四道闸门全绿才算通过;smoke 未纳入 CI(需要起服务与浏览器,本地/发布前执行)。vitest.config.ts:environment happy-dom、globals: true、include src/**/*.{test,spec}.{ts,tsx}、testTimeout: 20_000(注释:图表库 JSON 体量大,懒加载导入在 node 下偏慢)、setupFiles vitest.setup.ts(全局 stub 掉 element-plus 的 message 样式模块,注释说明 vitest 下 element-plus 被外置化导致 CSS 无法加载)。
19 个测试文件的覆盖矩阵:
| 层 | 测试文件 | 锁定内容 |
|---|---|---|
| 数据 | src/data/charts/__tests__/charts-data.test.ts、three-data.test.ts | 三库条数 348/351/989;19 必需键;代码字段守恒;SFC↔JSON 互不缺 |
| 数据访问 | src/api/__tests__/chart-examples.test.ts | 筛选/分页/搜索语义 |
| 渲染链 | src/chart-examples/__tests__/EChartDemo.test.ts、src/components/chart-examples/__tests__/{ChartExampleRenderer,VueSfcRenderer}.test.ts | 执行契约、loaderKind 分派、SFC 沙箱编译 |
| 视图 | src/views/__tests__/{ChartLibraryPage.pagination,ChartExampleDetailPage.breadcrumb,DataVisualizationHomePage.navigation,NotFoundPage}.test.ts、src/views/bigscreen/__tests__/{BigScreenDetailPage.back,bigscreenThumbnails}.test.ts | 分页行为、面包屑与真实路由表回归、导航跳转、404、返回行为、缩略图存在性 |
| 配置 | src/config/__tests__/bigscreenComponents.test.ts | 大屏清单↔组件映射一一对应 |
| 组合函数 | src/composables/__tests__/{useTheme,useToast,useListFilter}.test.ts | 主题三态、toast、筛选防抖 |
| 通用组件 | src/components/common/__tests__/{AppButton,AppInput}.test.ts、src/components/layout/__tests__/AppHeader.nav.test.ts | UI 原子件与导航状态 |
覆盖率:@vitest/coverage-v8 已安装,执行 pnpm vitest run --coverage 即可产出 V8 覆盖率报告;package.json 未定义独立 coverage script、vitest.config.ts 未配置 coverage 阈值(⚠️ 待确认:覆盖率门禁是否在其他流水线中设置)。
pnpm test:smoke(node scripts/smoke.mjs)需要先起服务:pnpm dev 或 pnpm build && pnpm preview,默认 BASE_URL=http://localhost:5566,可用环境变量覆盖(BASE_URL=https://xxx node scripts/smoke.mjs)。
脚本流程(scripts/smoke.mjs,104 行):
goto(waitUntil: networkidle) → HTTP 状态断言 → pageerror 收集 → 等待 2500ms → 截图存 smoke-shots/{name}.png。document.querySelectorAll('canvas').length > 0("echarts 渲染证据")。waitForFunction 显式轮询 canvas 出现(20s 超时,注释解释固定等待会被 echarts-vendor 冷启动拖垮),且 getByText('渲染失败') 计数为 0。.scm 出现 → 关闭 → waitForSelector({state:'detached'}),验证 Transition 完整走完。/charts/echarts/gl-lines3d-airline-on-globe 覆盖"api-async 求值 + echarts-gl 预载 + fetch 数据文件"整条兜底通路,canvas > 0、无"渲染失败"、无"加载资源失败"。page.on('request'),命中 jsdelivr.net|aliyun.com|youbao.xyz|alipayobjects.com 即收集,最后统一判失败。smoke-shots/ 里的截图是运行产物(.gitignore 已忽略,评审时人工目检),不是测试输入。
| 脚本 | 用途 |
|---|---|
smoke.mjs | Playwright 冒烟(见 4.2) |
slim-chart-json.mjs | JSON 瘦身:有本地 SFC 的条目删除 componentCode/optionCode/initCode |
gen-three-registry.mjs | curio → 本项目 three 示例迁移(组件拷贝/JSON 生成/资产裁剪/一致性校验) |
gen-thumbs-webp.mjs | 四个缩略图目录 PNG→WebP(宽 640、质量 78,--prune 删原图) |
gen-bigscreen-thumbs.mjs / convert-three-thumbs.mjs / fetch-three-models.mjs | 大屏缩略图生成 / three 缩略图转换 / 模型下载 |
sweep-fallback.mjs / heal-sweep.mjs / repair-antv-fallback.mjs / sweep-three.mjs / audit-bigscreen.mjs | 兜底条目巡检、修复与审计类治理脚本 |
以"新增一个 ECharts 案例"为主线(antv/three 差异附后)。
在 src/chart-examples/echarts/{分类目录}/{id}.vue 新建文件。id 即身份:必须与后续 JSON 条目、清单条目、缩略图文件名完全一致(kebab-case,全站唯一)。模板遵循 docs/CODE_TO_VCHART_GUIDE.md 规范:
<template>
<div class="demo-container">
<EChartDemo :option="option" />
</div>
</template>
<script setup>
import EChartDemo from '../../EChartDemo.vue'
// 简单示例:普通对象;需动态更新则用 ref 并整体/局部替换 option.value
const option = {
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
yAxis: { type: 'value' },
series: [{ data: [150, 230, 224], type: 'bar' }],
}
</script>
<style scoped>
.demo-container {
position: relative;
width: 100%;
height: 100%;
min-height: 280px;
border-radius: inherit;
overflow: hidden;
}
</style>要点:需要 echarts.registerMap / setInterval / 事件交互时,不要在 SFC 里自行 init——用 EChartDemo 的 setup(ctx) 钩子(返回清理函数)或按手册改写为 <v-chart> 响应式写法;GL 系列在脚本内 import 'echarts-gl'(放在 echarts import 之后)。
在 src/chart-examples/manifest.ts 的 echartsLocalManifest 数组追加(按 id 字母序插入,与现有风格一致):
{
id: 'my-new-demo',
title: '我的新示例',
category: '柱状图', // 尽量复用已有分类(37 类之一),新分类会出现在侧栏
tags: ['柱状图', '动画'],
},注意:manifest.ts 头部标注"由 server/scripts/gen-charts-local.ts 自动生成,请勿手改"——手工追加条目是当前仓库内的实际可行做法(⚠️ 待确认:server 生成器仓库未随项目提供,无法验证再生成是否会覆盖手工条目;建议追加后 git 提交留痕)。
在 src/data/charts/echarts.json 追加完整 schema 条目(19 字段,见 04 篇第 1 节)。这一步不可省略:列表页数据完全来自 JSON(getChartExamples),没有 JSON 条目的案例不会出现在列表里。由于存在本地 SFC,按瘦身规则不要写 optionCode/componentCode/initCode;loaderKind 填 option(或 api-async,与同类条目保持一致);thumbnail 可先置 null(列表回落 /chart-thumbs/{id}.webp)。
生成 640 宽 WebP 放入 public/chart-thumbs/{id}.webp(推荐直接用 node scripts/gen-thumbs-webp.mjs 的同参数规格:宽 640、质量 78、保留透明通道)。无缩略图时列表卡片显示 📊 占位,功能不受阻。
src/data/charts/__tests__/charts-data.test.ts 中 FILES 常量把 echarts 条数硬编码为 expected: 348——新增 1 条后改为 349。若新增的是 antv/three 案例,对应修改 351/989。然后跑 pnpm test 确认守恒断言全绿(该测试还会校验 SFC↔JSON id 互不缺,第 1、3 步任何遗漏都会在此暴露)。
pnpm dev
# 浏览器访问 http://localhost:5566/charts/echarts
# 1) 列表出现新卡片(搜标题或按分类筛选)
# 2) 点进详情:canvas 渲染正常、无"渲染失败"
# 3) 打开源码弹窗:内容与 SFC 一致;下载按钮可用
pnpm typecheck && pnpm lint && pnpm test
# (推荐)起 dev 后执行冒烟,必要时把新案例加进 scripts/smoke.mjs 的 PAGES
pnpm test:smokeonMounted 中 new Plot(containerRef.value, options) + render(),无需 EChartDemo);manifest 加进 antvLocalManifest;JSON 加进 antv.json(loaderKind: 'antv-config')。node scripts/gen-three-registry.mjs 从 curio 源迁移(自动完成组件拷贝到 src/chart-examples/three/{source}/{category}/、three.json 与 three-manifest.ts 生成、资产裁剪拷贝与四项校验);手工新增需同步三处——SFC(基于 ExampleScene 壳)、three-manifest.ts、three.json(loaderKind: 'local',按需 globals.ASSET_HOST),并保证资产放入 public/ 对应根路径。src/views/bigscreen/{name}/{Name}Page.vue(根节点用 DatavContainer),同时在 src/config/bigscreenData.ts 与 src/config/bigscreenComponents.ts 各加一条(两处键必须一致,测试锁定),缩略图放 public/bigscreen-thumbnails/{id}.webp,并跑 src/config/__tests__/bigscreenComponents.test.ts 验证。charts-data.test.ts 的 expected 数字(历史先例:apexcharts/chartjs/d3 三库于 2026-10 整体删除,README 有记录)。| 现象 | 首查位置 |
|---|---|
| 图表空白无报错 | 是否漏注册 <v-chart>(src/main.ts 全局 VChart);容器高度是否为 0(VueSfcRenderer 的 compileWhenReady 有 60 帧高度等待逻辑) |
| 详情页"渲染失败:图表实例未初始化" | EChartDemo.waitForInstance 超时——检查 v-chart 挂载链路或 echarts chunk 加载 |
| 兜底条目提示"加载资源失败" | public/echarts-data/ 缺对应数据文件(smoke 第 5 项断言即覆盖此场景) |
| 列表页出现"缺失 componentPath"红章 | JSON component 条目渲染时未提供 componentPath(VueSfcRenderer 的徽章逻辑) |
| 快速切换示例后画面串台/实例泄漏 | 确认改动未破坏 renderSeq/compileSeq 代次保护(01 篇 5.4) |
| WebGL 报 context 丢失 | three 壳的 forceContextLoss 是否被绕过(反复重挂载撞浏览器 context 上限) |
| 刷新非根路径 404 | 静态服务器未配置 SPA fallback 到 index.html |