项目:html5-dataviz | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
本项目无任何运行时服务端配置,全部配置集中在仓库根部的九个文件里:vite.config.ts(构建与依赖重映射)、package.json(脚本与依赖)、tsconfig.json + tsconfig.node.json(类型检查)、vitest.config.ts + vitest.setup.ts(单测)、eslint.config.js(lint)、.prettierrc(格式化)、index.html(入口与字体)。另有三个生成物不可手改:auto-imports.d.ts、components.d.ts(unplugin 产物)、以及 dist/。环境变量仅四个且全部有默认值(见第 8 节),没有 .env 文件。理解这套配置的主线是:《08-安全与权限设计》的"外链清零"与《10-性能设计与优化》的"懒加载一切"两条架构约束如何落到每一个配置段上。
配置间存在清晰的因果链,读配置时按链索骥比逐文件孤立理解高效得多:index.html 的字体外链是"外链清零"约束的唯一例外(渐进增强决策);vite.config.ts 的 alias 段是同一约束在 import 层的执行器;manualChunks 段则是"懒加载一切"在 chunk 图上的投影;tsconfig.json 的 paths 三条别名与 vite alias 的前三条保持一一对应——两套系统(构建期 rollup 解析与类型期 tsc 解析)各自需要一份映射,改任何一条别名都必须双处同步,否则会出现"构建能过、类型报错"或反之的劈叉;vitest.config.ts 单独维护了一份精简版 alias(只有 @ 一条),因为它不走 vite.config.ts(单测不需要 CDN 重映射与分包),这意味着新增全局别名时实际上要改三处。eslint.config.js 与 tsconfig.json 的 exclude 面向同一批"生成资产"(chart-examples、three),两份豁免清单的同步关系在《13-技术债务与演进建议》第 7、8 条有专门讨论。
五个插件一次注册:vue()(SFC 编译)、tailwindcss()(Tailwind 4 的 Vite 原生集成,配合 src/assets/main.css 的 @theme inline 令牌体系)、AutoImport({ resolvers: [ElementPlusResolver()] }) 与 Components({ resolvers: [ElementPlusResolver()] })(unplugin 双件套,机制见第 5 节)。没有 mock、没有压缩自定义、没有 PWA——配置面刻意保持最小。
| 别名 | 目标 | 作用 |
|---|---|---|
@ | src/ | 全站通用路径别名 |
@ioftv | src/views/bigscreen/ioftv-screen | ioftv 大屏自成体系的专属别名 |
@three-examples | src/chart-examples/three | three 迁移壳组件的共享引用别名 |
three/examples/jsm/libs/ecsy.module.js | src/chart-examples/three/vendor/ecsy.module.js | npm three@0.186 已移除 ecsy,3 个 webxr 手势示例仍需,用 curio 快照续命 |
https://cdn.jsdelivr.net/npm/opentype.js@1.3.4/+esm | node_modules 对应包 | TTFLoader 裸写 CDN import 的重映射 |
https://cdn.jsdelivr.net/npm/mp4box@2.3.0/+esm | node_modules 对应包 | demuxer_mp4 同上 |
three/examples/jsm/physics/AmmoPhysics.js | src/chart-examples/three/vendor/AmmoPhysics.js | 运行时 import CDN 的 ammo.wasm 字符串 alias 拦不住,本地化副本改 AMMO_PATH,wasm 在 public/vendor-ammo/ |
https://cdn.jsdelivr.net/npm/lottie-web@5.13.0/+esm | node_modules 对应包 | 组件源码级 CDN 静态 import 重映射 |
https://cdn.jsdelivr.net/npm/@gsimone/three-raycaster-helper@0.1.0/... | node_modules 对应包 | 同上 |
https://cdn.jsdelivr.net/npm/@jgphilpott/polyslice@26.4.0/... | node_modules 对应包 | 同上 |
注释里最关键的工程教训是 opentype.js 那条:TTFLoader/demuxer_mp4 裸写的 CDN import 会被 rollup 提升进 ExampleScene 共享 chunk,导致几乎所有示例页面都发外链——单点污染全局,这正是 smoke 外链断言存在的价值。
optimizeDeps.esbuildOptions.target: 'esnext' 是一个隐性必配项:three 官方包(capabilities/WebGPU.js 等)含顶层 await,esbuild 默认 target 下预构建直接崩(注释原话),build 侧对应 build.target: 'esnext'。build 段无 sourcemap、无压缩自定义,核心是 rollupOptions.output.manualChunks 五桶函数——逐行规则、桶间顺序语义与 9.3MB 事故复盘见《10-性能设计与优化》第 2 节,此处只强调两条不变式:src 代码永不进 vendor 桶(函数首行对非 node_modules 返回 undefined)、echarts-gl 判定必须先于 echarts(否则 echarts-gl 落错桶)。
| 脚本 | 命令 | 说明 |
|---|---|---|
dev | vite --port 5566 | 端口写死 5566(smoke 默认 BASE_URL 与之一致) |
build | NODE_OPTIONS='--max-old-space-size=8192' vite build | 1617 个 SFC 的 glob 切分吃内存,8GB 上限 |
build:fast | vite build | 免大内存备选(小内存机器) |
preview | vite preview | 预览 dist(默认 4173 端口) |
lint / lint:fix | eslint . / eslint . --fix | flat config |
format | prettier --write "src/**/*..." | 仅 src 目录 |
typecheck | vue-tsc --noEmit | CI 阻塞项;注意 exclude 掉 chart-examples/three(见《13-技术债务与演进建议》第 7 条) |
test / test:watch | vitest run / vitest | 19 个测试文件 |
test:smoke | node scripts/smoke.mjs | 需先起 dev 或 preview |
依赖面两个值得注意的点:dependencies 里 @vue/compiler-sfc 是运行时依赖(沙箱编译在浏览器执行,不是构建工具),sharp/playwright/shiki 等只在脚本或按需 chunk 中使用;three 生态包约 25 个(postprocessing、three-mesh-bvh、@takram 系列、物理引擎等),全部被 manualChunks 的 /three 规则收进 three-vendor 桶。
脚本设计的两个意图值得一读。其一,build 与 build:fast 并存不是冗余而是部署弹性:8GB 内存上限在 CI 与开发机(通常 16GB+)上无感,但内网交付常发生在配置有限的跳板机上,build:fast 让构建至少"能跑完"——代价是 glob 切分可能更频繁触发 GC,构建时间显著变长。其二,test:smoke 是全脚本表里唯一需要"先起服务"的条目,却没有包一层自动化(如 npm-run-all 串接 dev 与 smoke),这是有意的:冒烟的 BASE_URL 语义要求它能对准任意已部署站点(内网验证场景),把服务生命周期绑进脚本反而收窄用途,见《06-部署运行》第 4.2 节的使用方式。format 只覆盖 src 目录(不含 scripts/ 与配置文件)是 prettier 范围的刻意收缩——脚本区代码风格由 eslint 放宽规则兜底,避免大批量格式化 diff 污染治理脚本的历史可读性。
主 tsconfig 的要点:strict + noUnusedLocals + noUnusedParameters 全开(示例类项目罕见的严格度);moduleResolution: 'bundler' 与 resolveJsonModule(三个 JSON 数据集的类型化 import);paths 三条(@/*、@ioftv/*、@three-examples/*)与 vite alias 一一对应;types 含 vite/client(import.meta.glob、import.meta.env 类型)与 element-plus/global;exclude 中 src/chart-examples/three 是最特殊的一条(类型盲区,理由与折中方案见 13 篇第 7 条)。tsconfig.node.json 是极小的 composite 子项目,仅管 vite.config.ts 自身(types: node),保持"构建配置"与"应用代码"两套类型环境隔离。
这两个文件是 unplugin-auto-import 与 unplugin-vue-components 在 dev/build 时自动生成的类型声明产物(.gitignore 已忽略,说明仓库认识到它们是本地产物)。机制分两条线:
auto-imports.d.ts 的 declare global 块是空的——项目代码风格是显式 import(可读性优先),该插件实际只承担 Element Plus 的 API 转换副作用;src/components/** 与 Element Plus,在模板中使用而未显式 import 的组件自动按需注入。components.d.ts 的 GlobalComponents 接口当前登记了 29 个组件:AppButton/AppHeader/AppIcon/AppInput/CategoryChips/CodeBlock/CommonCodeBlock、chart-examples 双渲染器与 SourceCodeModal、datav 全族(DatavBorderBox/Container/CountTo/Decorations/Icon/Loading/Panel/ScrollTable/SvgIcon)、layout 五件(EmptyState/PageHeader/Pagination/SearchFilterBar/SkeletonGrid/TreeSidebar)、ElBreadcrumb 两个、RouterLink/RouterView。这是"为什么大屏模板里直接写 DatavContainer 不用 import"的答案。配套的一致性约定在 src/main.ts:Element Plus 的按钮/输入框样式被显式引入两条 CSS(element-plus/es/components/button/style/css 等),注释解释了原因——按需 resolver 只注入组件本体,样式在部分场景(AppButton/AppInput 显式 import 组件本体时)会漏,vitest.setup.ts 里 stub 掉 ElMessage 样式模块是同一问题的测试侧镜像(element-plus 在 vitest 下被外置化,其内部 CSS 无法被 node 加载)。这套"组件自动 + 样式显式"的混合模式是配置层最容易踩坑的点:新增公共组件时,模板不 import 可用,但样式要检查 main.ts 是否需要补显式引入。
这套机制还有两个容易被忽略的工程含义。其一,自动注册的范围是全量的:components.d.ts 里登记的 29 个组件在任何模板里都可直接使用,这让"组件被谁使用"无法从 import 语句推断,排查未使用组件或做 tree-shaking 分析时要以 unplugin 的注入行为为准(未用到的组件不进产物,无需手动清理声明)。其二,d.ts 是类型提示而非存在保证:components.d.ts 只在组件文件真实存在时类型才成立,若组件被删除而 d.ts 未重新生成(如直接编辑过),会出现类型可用、运行时报错——始终让 dev server 重跑生成而不是手补声明。对比一下全站唯一的 initSfcComponentRegistry(src/utils/sfc-modules.ts)就能看清两套注册体系的边界:unplugin 管构建期的模板编译注册,sfc-modules 管运行时沙箱的组件注册表,后者服务于浏览器端编译的 SFC(它们没有构建期的 import 解析),两者名字相似、机制正交,配置层只涉前者。
单测配置四要素:environment happy-dom(DOM 级组件测试无真浏览器)、globals: true(describe/it/expect 免 import)、include: ['src/**/*.{test,spec}.{ts,tsx}'](测试与源码同目录的 __tests__/ 约定)、testTimeout: 20_000——注释给出理由"图表库 JSON 体量大,懒加载导入在 node 下偏慢"(实测 echarts.json 1.22MB 的 JSON import 在 node 下确实秒级)。setup 文件全局 mock 一条:element-plus/es/components/message/style/css(理由见第 5 节)。coverage 配置缺席的现状与补齐建议见 13 篇第 4 条。
与 vite.config.ts 的关系上,单测配置是刻意独立而非疏漏:vitest 不复用 vite.config.ts(否则 CDN alias、manualChunks、unplugin 双件套都会被带进测试环境,拖慢启动且引入无关变量),只保留 @ 别名这一条测试真正需要的映射。代价是新别名需要双处维护(见第 1 节的三处同步提醒),收益是测试环境的最小化与确定性——19 个测试文件在 CI 的装依赖 + 跑完总时长能控制在分钟级,与这套精简配置直接相关。给测试环境加配置的判断标准:只加"测试正确性必需"的(如 setup 里的 stub),能用源码层注入(如 sfcRuntime 的 setSfcCompiler 测试替身口)解决的就不要动配置。
flat config 结构(tseslint.config 串联):ignores 列表是全文件最重要的一段治理记录——dist/node_modules/*.d.ts/public 常规项之外,src/chart-examples/**(生成资产)与 20 个大屏迁移存量目录被逐条豁免,business/report/dashboard 三个业务大屏显式不豁免(注释:保持 lint 覆盖)。规则面三处值得注意:@typescript-eslint/no-explicit-any 提升为 error(严格化的标志性配置);ban-ts-comment 关闭(允许 @ts-nocheck,sfcRuntime.ts 顶部就靠它豁免动态编译产物的类型噪声);scripts/ 目录单独一块放宽(no-require-imports/no-unused-vars/no-undef 关闭 + 补 node 全局)。Vue 规则用 flat/essential 档并关闭 multi-word-component-names(EChartDemo 等单词名组件合法)。.prettierrc:无分号、单引号、printWidth 100、es5 尾逗号、LF——与 eslint-config-prettier 收尾(必须放在配置数组最后以关闭冲突规则)配合,lint 与 format 无冲突。
| 变量 | 读取位置 | 默认 | 用途 |
|---|---|---|---|
BASE_URL(进程级) | scripts/smoke.mjs、scripts/sweep-fallback.mjs | http://localhost:5566 / :4173 | 冒烟/清扫目标站点地址 |
VITE_SFC_RUNTIME_DEBUG | src/utils/sfcRuntime.ts(仅 DEV) | 未设置 | 沙箱编译详细日志(调试入口见 12 篇第 4 节) |
NODE_OPTIONS | package.json build 内联 | — | 8GB 构建内存 |
import.meta.env.{MODE,DEV,PROD,BASE_URL} | sfcRuntime 的 env 替换、sfc-modules 资产解析 | Vite 内建 | 沙箱内 import.meta.env 的字面量替换源 |
无 .env 文件、无 VITE_ 前缀自定义业务变量——纯静态站的配置收敛度极高。部署侧三个配置点(操作细节见《06-部署运行》第 3 节):SPA fallback(createWebHistory 要求未命中路径回退 index.html)、端口约定(dev 5566 / preview 4173 / smoke 默认前者)、CI 四道闸门(.github/workflows/ci.yml:pnpm 10 + node 22 + frozen-lockfile 安装 → lint → typecheck → test → build;smoke 未纳入,现状与建议见 13 篇第 10 条)。子路径部署需要 vite base 配置(当前未设置,影响面见 12 篇速查表第 13 行)。
部署配置里有一个值得展开的推理题:为什么本项目可以放心用 history 路由而不需要 hash 模式兜底?因为它的部署形态被明确收敛为"自有可控的静态服务器"(内网交付、Nginx/静态托管均可配 fallback),而不是"不可控的任意静态空间"(如对象存储裸开、无重写能力的 CDN)。createWebHistory 换来的是干净的 URL 与正常的服务端日志语义;如果未来出现必须部署到无 fallback 能力环境的场景,改 src/router/index.ts 为 createWebHashHistory 是唯一改动点,路由表(routes.ts)无需任何变化——这也是路由表独立成文件的又一个收益。另一处部署相关的事实:dist 里 *.wasm、大体积模型与 JSON 的 MIME 正确性依赖静态服务器的默认映射,Nginx 默认即可,个别极简静态服务器(如某些嵌入式 httpd)会对 wasm 返回 text/plain 导致浏览器拒载,内网选型时应把"MIME 支持完整"列为服务器准入条件(呼应《08-安全与权限设计》第 7 节的前提表)。
改动根配置时的最小回归集(按改动文件给出必须跑的验证):改 vite.config.ts alias → pnpm test:smoke(外链断言 + 渲染探针);改 manualChunks → smoke + Network 目检首屏 preload 列表;改 tsconfig paths/exclude → pnpm typecheck;改 vitest 配置 → pnpm test;改 eslint ignores → pnpm lint 且确认豁免面没有扩大;改 package.json 依赖 → pnpm install && pnpm test && pnpm test:smoke 三连(依赖变更是外链回归与守恒测试失败的最高发来源)。两个生成文件(auto-imports.d.ts/components.d.ts)在任何情况下都不手改——它们由 dev/build 自动重写,手改内容会在下次构建丢失。