项目:html5-examples | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
本篇把根 package.json(前端)与 server/package.json(后端)的全部直接依赖逐个过一遍,每条回答四个问题:它被谁用、为什么选它、换掉它要付什么代价、升级它会踩什么坑。《00-项目总览》§3 的技术栈表回答"有什么",本篇回答"为什么与怎么样";两者冲突时以本篇的实测为准(本篇基于 2026-10-06 的 package.json 实文)。
统计口径有三条约定。第一,"使用位置"给的是代表性消费点而非穷举——像 dayjs 这类散布全站的工具只列入口级位置;第二,"替代方案"评估的是当年做选型时真实存在的对手,不做上帝视角的翻案;第三,^ 语义意味着实际安装版本可能高于声明值,本文版本号全部抄自 package.json 声明,锁定版本以 pnpm-lock.yaml 为准(未逐条核对实际解析版本,⚠️ 待确认)。
读依赖表之前先建立一个总体判断:这个项目的依赖结构是"前端重、后端轻、两侧都不虚胖"。前端 32 个运行时依赖里约半数服务于"Canvas 九库 + 动效四板块 + 编辑器高亮"这些内容板块的硬需求;后端 22 个运行时依赖是标准的 NestJS 全家桶及其配套(express/reflect-metadata/rxjs/class-validator/class-transformer)加四个领域特需(ali-oss/mysql2/nanoid/dotenv)。真正的冗余集中在两处——前端为已拆分板块保留的图表库残余,以及后端冻结模块挂着的特需依赖——第 7 节健康度评估会分别点名。
| 依赖 | 版本 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|
| vue | ^3.5.0 | UI 框架 | 全站 | <script setup> 生态成熟 | React/Solid | 大版本升级需过 vue-tsc 全量检查 |
| vue-router | ^4.3.0 | 路由 | src/router/index.ts | 与 Vue3 深度集成、懒加载原生支持 | TanStack Router | v4 内小版本风险低 |
| pinia | ^2.1.0 | 状态管理 | src/stores/(仅 2 个 store) | Options Store 约定(ADR-2) | 自研 composable 状态 | 升 v3 需验证 devtools 兼容 |
| @vueuse/core | ^14.3.0 | 组合式工具 | useListFilter(watchDebounced)、useRecentExamples(useStorage)、usePreview(useClipboard) | 避免手写这些易错原语 | 自研(防抖/存储已有自研先例) | API 命名偶有破坏性调整 |
| @vue/compiler-sfc | ^3.5.38 | 浏览器内 SFC 编译 | src/utils/sfcRuntime.ts(538 行) | 与 vue 同源版本保证编译一致 | 自建沙箱编译器 | 必须与 vue 版本同步升级 |
| xiaoye-ui | ^6.1.0 | 自有 unstyled 组件库 | src/main.ts 全局装配 | 团队自有资产、PassThrough 可控样式 | naive-ui/reka-ui | 内部库随源码演进,风险自持 |
| @guolao/vue-monaco-editor | ^1.6.0 | Monaco 的 Vue 封装 | src/components/common/code/MonacoCodeEditor.vue | 免手写 Monaco loader 生命周期 | 直接用 monaco-editor loader API | 与 monaco-editor 版本需匹配 |
| monaco-editor | ^0.55.0 | 代码编辑器内核 | SourcePane 编辑态 | 业界最强浏览器编辑器 | CodeMirror 6 | 大版本体积与 API 双波动,已独立 monaco-vendor chunk |
| shiki | ^1.0.0 | 代码高亮 | src/utils/highlighter.ts 单例 | TextMate 级精度、按语言内联注册控体积 | Prism(轻但精度低)、highlight.js | v1→v3 API 面貌全变(bundle 函数化),升级等于重写 highlighter |
| echarts | ^6.1.0 | 图表 | src/utils/echarts-setup.ts、sfc-modules.ts | 残余演示需求 | chart.js | 已独立 vendor chunk,升级不影响主路径 |
| echarts-gl | ^2.1.0 | 3D 图表 | sfc-modules.ts、types/echarts.d.ts | 同上 | — | 与 echarts 主版本耦合,最易失配 |
| vue-echarts | ^8.0.1 | echarts 的 Vue 组件 | src/main.ts 全局注册 v-chart | 声明式用法 | 手动 init/dispose | 跟随 echarts 大版本 |
| @antv/g2plot | ^2.4.35 | 统计图表 | sfc-modules.ts(SFC 运行时可用模块表) | 历史图表板块遗留 | echarts 覆盖大部分场景 | 仅 SFC 演示可达,可随拆分清理 |
| three | ^0.184.0 | WebGL 3D | src/views/svg-lessons/SvgBookSvgExtruder.vue 等 | 3D 演示硬需求 | babylon | 月度发版节奏,升级时 @types/three(devDep)严格同版 |
| gsap | ^3.15.0 | 动画引擎 | 动效库 GSAP 板块、MotionPathPlugin 等 | 时间线/路径动画事实标准 | WAAPI 原生、animejs | 必须保持 dedupe + optimizeDeps 全列(跨实例污染事故,vite.config.ts 13-17 行) |
| animejs | ^4.4.1 | 动画 | 动效库 | 补充 GSAP 之外的演示 | gsap | v3→v4 是全新 API |
| animate.css | ^4.1.1 | 现成动画类库 | src/config/effects/animate-css.ts | 纯 CSS 零 JS | 自写 keyframes | v5 变更类名前缀 |
| splitting | ^1.1.0 | 文本/元素拆分动画 | 动效库演示 | 拆字/拆行标配 | 自写 span 切分 | 稳定 |
| canvas-confetti | ^1.9.4 | 彩带特效 | 演示 | 单文件零依赖 | 自实现 | 稳定 |
| pixi.js | ^8.19.0 | WebGL 2D 渲染 | Canvas 板块 pixi-js 库 | 2D WebGL 性能标杆 | konva(Canvas 2D) | v7→v8 异步初始化破坏性变更 |
| fabric | ^7.4.0 | Canvas 对象模型 | Canvas 板块 fabric-js | 对象级画布编辑 | konva | v5→v6 ESM 重构曾是大坑 |
| konva | ^10.3.0 | Canvas 场景图 | Canvas 板块 konva-js | 声明式场景树 | fabric | 稳定 |
| leafer-ui | ^2.1.8 | 国产 Canvas 引擎 | Canvas 板块 leafer-js | 轻量现代化 | konva | 生态较小,配 @leafer-in 两插件 |
| @leafer-in/animate / state | ^2.1.8 | leafer 插件 | 同上 | 官方插件对 | — | 与 leafer-ui 同步升级 |
| p5 | ^2.3.0 | 创意编码 | Canvas 板块 p5-js | 教学友好 | — | 与 signature_pad 合并 canvas-misc-vendor |
| roughjs | ^4.6.6 | 手绘风格图形 | Canvas 板块 rough-js | 独特风格无替代 | — | 稳定 |
| signature_pad | ^5.1.3 | 签名板 | Canvas 板块 signature-pad | 专精单一功能 | 自实现压感 | 稳定 |
| dayjs | 1.11.21 | 日期 | src/utils/date.ts 及全站 | 体积小、插件化 | date-fns | 精确锁版(无 ^),插件全列 optimizeDeps |
| dompurify | ^3.4.11 | HTML 消毒 | src/utils/sanitize.ts | XSS 防御事实标准 | sanitize-html(重) | 跟随安全公告升级,勿滞后 |
| material-symbols | ^0.45.8 | 图标字体 | AppIcon ms: 词表 | Google 官方全量图标 | iconify 按需 | 稳定 |
| vuedraggable | 4.1.0 | 拖拽排序 | src/components/common/DraggableTree*.vue | 基于 Sortablejs 的 Vue 封装 | 原生 Sortablejs | 精确锁版;vue3 版本线(4.x)与 vue2 线不同仓库 |
这张表里藏着一个"体量分层"的事实:真正决定首屏的只有 vue/vue-router/pinia(manualChunks 的 vendor 组,vite.config.ts 89-97 行精确匹配),其余重型依赖全部有专属 chunk 或按需入口——第 2 节的重点展开会回到这个机制。
Vue 生态四件套的选型几乎没有悬念,真正值得记录的是三个使用纪律。第一,Pinia 被刻意限制在 2 个 store(exampleStore、authStore):全局状态只收"跨页面共享且需要响应式"的两样东西——示例目录与认证会话,页面级筛选状态一律走 URL query(ADR 的"URL 即状态",《01-系统架构》§5 模式四)。这使 store 不是状态垃圾场而是精心圈养的会话中枢。第二,@vueuse/core 是本项目"隐形的基础设施":300ms 防抖(watchDebounced)、localStorage 响应式(useStorage)、剪贴板(useClipboard)都出自它,而这些恰恰是最容易写出自研 bug 的原语;项目在 src/utils/debounce.ts 另有一份自研防抖(带单测),两者并存说明选型纪律是"组合式函数场景用 @vueuse,纯函数场景自研可测"。第三,@vue/compiler-sfc 与 vue 严格同版本(都声明 ^3.5.x):浏览器内 SFC 运行时(sfcRuntime.ts)编译产物直接跑在同页面的 vue runtime 上,两者版本错位会出现编译产物引用不存在的内部 API——升级 vue 时这是必须成对检查的依赖对,manualChunks 也把 compiler-sfc 与 esbuild 单独拆成 sfc-compiler chunk 隔离加载时机(vite.config.ts 63-70 行)。xiaoye-ui 作为团队自有库被 optimizeDeps.exclude 保持源码形态(vite.config.ts 146 行),避免预构建版本漂移——这是"自己的库自己编译"的明确表态。
编辑器链路是两条技术路线的并轨:Monaco 管编辑,Shiki 管展示。Monaco(monaco-editor + @guolao/vue-monaco-editor)只在 SourcePane 进入编辑态时挂载(MonacoCodeEditor.vue 仅编辑态渲染),配合 monaco-vendor 独立 chunk,"打开详情页只读"永远不下载 Monaco 的数百 KB(《10-性能设计》§3.2 样板一);封装层 @guolao 的价值是把 Monaco 的 AMD loader、worker 配置、生命周期销毁这些臭名昭著的脏活包成 Vue 组件。Shiki 则反着来:只读高亮必须廉价且处处可用,所以走 highlighter.ts 的单例 + 14 种语言内联注册 + 2 主题的极简配置(ADR-5),它比 Monaco 轻两个数量级但精度足够。两者的分界线是"可编辑性"而非"语言支持"。升级风险上,Shiki v1 到 v3 的 API 已发生结构性变化(createHighlighter 转向 bundle 函数族),当前锁 ^1.0.0 是务实选择,升级等于重写 highlighter.ts,应单独立项;Monaco 的月度发版风格则要求 @guolao 封装层确认兼容后再动。
echarts / echarts-gl / vue-echarts / @antv/g2plot 是三个已拆分姊妹项目留下的残余依赖:图表内容主体已迁往 html5-dataviz,本项目仅剩 SFC 运行时演示与个别页面还在引用(src/utils/sfc-modules.ts 的可用模块表、echarts-setup.ts 的按需初始化)。项目没有急于卸载它们,而是用两道机制把影响锁死:manualChunks 三级拆分(echarts-gl-vendor / echarts-stat-vendor / echarts-vendor)保证"不进首屏",echarts-setup.ts 的文件头注释记录了从"main.ts 全量导入(~1.1MB 进入口)"改为"路由级加载"的优化史(《10-性能设计》§3.2)。健康度视角(第 7 节):这组依赖处于"低频使用、中额体积、低风险"的灰色地带,清理收益主要是安装体积与认知负担,而非运行时性能;若 SFC 演示对图表的需求可以收敛,卸载 @antv/g2plot 是最安全的第一刀。
九个 Canvas 库加三个动画族(GSAP/animejs/animate.css)合计 13 个依赖,全部服务于具体的演示内容——这是"内容型站点"依赖结构的典型样态:依赖数量由内容清单而非技术架构决定。项目为此付出的工程代价集中在构建治理:19 组 manualChunks 里约 10 组在为它们划界,注释里"每个示例只引入自己的库"一句就是这套拆包的设计总纲(vite.config.ts 108 行)。其中 gsap 的治理成本最高——resolve.dedupe + optimizeDeps 全列插件入口是双保险,注释记录的事故(MotionPathPlugin 跨实例污染抛 "Indexed property setter")说明这类"一个库被拆成多个物理 chunk"的策略与"库内全局单例"的假设天然冲突,未来给 pixi/fabric 等库新增插件子路径时也应预防同类问题。升级风险上最需要盯的是 pixi.js v8 的异步初始化与 fabric v7 的 ESM 化——两者都曾以破坏性重构著称,升级时应只动一个库并全量回归对应板块演示。
| 依赖 | 版本 | 用途 | 备注 |
|---|---|---|---|
| vite / @vitejs/plugin-vue / plugin-vue-jsx | ^5.0.0 / ^5.0.0 / ^5.1.6 | 构建 | JSX 供个别演示组件 |
| tailwindcss / @tailwindcss/vite | ^4.0.0 | 样式 | v4 无 config 文件,主题在 main.css @theme |
| typescript / vue-tsc | ^5.0.0 / ^3.3.8 | 类型 | strict 全量;vue-tsc 是 build 第一道闸 |
| vitest / @vitest/coverage-v8 / happy-dom | ^2.1.9 / ^2.1.9 / ^20.11.1 | 测试 | 覆盖率限定 utils+composables(《09-测试》§1.2) |
| @vue/test-utils | ^2.4.11 | 组件测试 | 仅 EmptyState/SiteFooter 两处 |
| eslint / typescript-eslint / eslint-plugin-vue / eslint-config-prettier / eslint-plugin-prettier / @eslint/js / globals | ^10.x 系 | lint | flat config,CI 门禁一 |
| prettier | ^3.5.3 | 格式化 | 无分号/单引号/行宽 100/LF |
| lint-staged | ^17.3.0 | 提交钩子 | 配 package.json 内嵌配置 |
| tsx | ^4.7.0 | 脚本运行 | pnpm extract 与 scripts/ 约 130 个历史脚本 |
| playwright / @playwright/test | ^1.61.0 | GUI 测试截图 | 与 puppeteer 并存(见 §7) |
| puppeteer | ^25.1.0 | 历史截图脚本 | 《13-技术债务》第 8 条建议收敛到 playwright |
| pg / @types/d3-geo / @types/uuid / @types/dompurify / @types/three | — | 历史脚本遗留与库类型声明 | 前端运行时不直连 PG(《14-配置》§4.2);@types/three 服务 three 同版约束 |
devDependencies 里值得点名的不是"有什么"而是"多什么":puppeteer 与 playwright 双浏览器自动化并存、pg/@types/d3-geo 等"前端项目里的数据库驱动与图表类型"都是历史内容工程(抓取、截图、导入脚本)的沉积物。它们不进产物、不影响运行时,但每晃一次 pnpm install 都要付安装体积与安全通告的注意力成本——第 7 节把它们计入"可清理项"。
| 依赖 | 版本 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|
| @nestjs/common / core | ^10.3.0 | NestJS 框架内核 | 全部模块 | 模块化 + 装饰器 + DI 一体 | Express 裸写 / Fastify | v10→v11 需过 Express 5 适配 |
| @nestjs/platform-express | ^10.3.0 | HTTP 平台 | main.ts | 默认平台最稳 | fastify 适配器 | 跟随 core |
| express | ^5.2.1 | HTTP 服务器内核 | 经 platform-express 间接使用(@Res 类型直接引用) | Nest 默认平台绑定的显式声明 | — | v4→v5 的类型包差异需与 @types/express 对照(devDep 锁 ^4) |
| @nestjs/config | ^3.1.0 | 环境配置 | app.module.ts(isGlobal,envFilePath '.env') | 类型化读取 + 校验钩子 | 手读 process.env | 低 |
| @nestjs/typeorm / typeorm | ^10.0.1 / ^0.3.19 | ORM | 27 实体 + 11 迁移 | 迁移体系成熟、QueryBuilder 参数化 | Prisma(需换迁移体系)、knex | 0.3.x 内稳定;升级关注 QueryBuilder 行为 |
| @nestjs/jwt | ^10.2.0 | JWT 签发校验 | auth.service | 官方封装 passport-jwt 底层 | jsonwebtoken 直用 | 低 |
| @nestjs/passport / passport / passport-jwt | ^10.0.3 / ^0.7.0 / ^4.0.1 | 认证策略 | strategies/jwt.strategy.ts | Nest 系标准姿势 | 手写守卫解析 | 低(passport 生态趋于冻结,反而是稳定性优点) |
| @nestjs/serve-static | ^5.0.5 | 托管 /admin 静态页 | app.module.ts | 一行挂载 | nginx 别名 | 低 |
| pg | ^8.11.3 | PostgreSQL 驱动 | TypeORM 底层 | typeorm 官方支持 | — | 低 |
| class-validator / class-transformer | ^0.14.0 / ^0.5.1 | DTO 校验与变换 | 全部 DTO + 全局 ValidationPipe | 与 Nest 管道原生集成 | zod(需自建管道) | 升级关注 forbidNonWhitelisted 行为一致性 |
| rxjs | ^7.8.1 | Nest 响应式底座 | 框架隐式 | 框架依赖 | — | 跟随 core |
| reflect-metadata | ^0.2.1 | 装饰器元数据 | 入口隐式 | 装饰器方案前提 | — | 与 TS 版本实验性装饰器配置耦合 |
| bcryptjs | ^3.0.3 | 密码哈希 | auth.service(cost 12) | 纯 JS 免原生编译 | bcrypt(原生,快但需编译) | 低 |
| nanoid | ^5.1.16 | 短 ID 生成 | server/src/common/utils/id.util.ts(generateId) | 比UUID短且无连字符 | crypto.randomUUID | v4→v5 改纯 ESM,node 场景需确认 |
| ali-oss | ^6.23.0 | 阿里云 OSS | modules/oss/oss.service.ts | 阿里官方 SDK | 自签 HTTP 直传 | 关联冻结模块(§6) |
| mysql2 | ^3.23.4 | MySQL 只读查询 | modules/run/run.service.ts | run 通道需求 | — | 关联冻结模块(§6) |
| dotenv | ^17.4.2 | .env 读取 | 仅 server/scripts/ 历史导入脚本 | 脚本脱离 Nest 也能读 env | node --env-file | 主进程不 import(ConfigModule 自带解析),属脚本依赖 |
devDependencies(@nestjs/cli、ts-node、tsx、typescript ^5.3、@types 系列、playwright)为常规配置,两点备注:playwright 出现在后端是为了 server/scripts/ 的历史截图脚本(实测 2026-10-06:253 个 .ts 脚本中 16 个引用 playwright,多为截图/缩略图产物脚本);server 的 lint script 是 --fix 形态无纯 check(《13-技术债务》第 9 条)。
后端选型的核心判断只有一个:用 NestJS 的"约定密度"换团队规模的"纪律密度"。一人维护的后端最容易退化成"一个 routes 文件里塞全部逻辑",Nest 的模块四件套(controller/service/entity/module)+ 全局管道 + 全局守卫把结构约定自动化了——本项目的 controllers 高度一致(@Controller('api/<name>') + @Public() 显式放行 + 写端点 AdminGuard),正是框架约定的红利(《03-接口设计》§2 的三个"统一")。TypeORM 的选择则与 Nest 生态绑定(@nestjs/typeorm 官方集成),它的QueryBuilder 参数化满足了注入防护基线(《08-安全与权限设计》§4),迁移体系支撑了 11 个结构演进节点(《04-数据模型》§4.3);代价是 0.3.x 的 API 面较大且部分行为(如 save 的级联语义)需要经验,团队以"实体三惯例"(《04-数据模型》§4.3 末段)对冲。认证栈(passport + passport-jwt + @nestjs/jwt)在 2026 年看略显繁琐——JWT 中间件手写不过百行——但它换来的是"宽容模式守卫、策略可替换"的弹性,jwt.strategy.ts 的 Bearer 优先/cookie 兜底双提取器(《08-安全与权限设计》§2.1)就是站在 passport 抽象上实现的。整体升级策略建议:Nest 系 10.x 全家保持同一节奏升到 v11(单次集中迁移优于逐包漂移),TypeORM 与 pg 在迁移测试(up/down 幂等)护航下跟进。
《13-技术债务》第 2 条的消费方审计结论是:examples、chart-examples、visual-demos(列表端点)、knowledge(7 子模块)、oss、run 六处后端资产当前没有前端消费方。它们拖住的特需依赖如下,在这些模块去留决策落地前,以下依赖不应单独升级也不应卸载(捆绑纪律):
| 依赖 | 服务模块 | 模块状态 | 处置建议 |
|---|---|---|---|
| ali-oss + @types/ali-oss | knowledge 域配套 oss 模块(/api/knowledge/oss) | 前端、三姊妹项目与 /admin 静态页均无引用(2026-10-06 复核) | 随 knowledge 域去留一并决策;保留期间跟随安全通告 |
| mysql2 | run 模块 MySQL 查询通道 | devtools 调用方在现源码找不到(2026-10-06 复核 devtools/dataviz/games 与 /admin 均无 /api/run 调用,⚠️ 待确认 仅剩未开源分支) | run 通道 fail-closed 守卫在,保留成本低;若确认弃用连依赖一起删 |
| nanoid | common/utils/id.util.ts(generateId) | 活跃(auth 的 refresh token id、captcha 均用) | 不在本清单删除范围,列出仅为澄清:它是公共工具而非冻结模块专属 |
需要澄清:examples/chart-examples/visual-demos/knowledge 四个模块本身没有引入额外依赖(复用 Nest 系全家桶),所以"冻结模块的依赖成本"实际上只有 ali-oss 与 mysql2 两项——这也是为什么第 7 节把它们标记为"低危冗余"而非"健康问题"。
总体评级:B+(健康,有三处可预期的清理项与两处高危关注点)。 分项如下。
前端运行时(A-):32 项依赖无一冗余于首屏——manualChunks 与按需加载把体量责任精确分摊到消费板块,这是同体量项目里少见的纪律。扣分项一:@antv/g2plot 与 echarts-gl 的"活化石"属性(§2.3),属于战略性遗留而非技术失误;扣分项二:puppeteer/playwright 并存与 pg/@types/d3-geo 等 devDependencies 沉积(《13-技术债务》第 8 条),建议随专项一清理,预计可减 200MB 级安装体积(⚠️ 待确认 实测值)。
后端运行时(B):Nest 系 + TypeORM 的组合对当前规模略重但约定收益真实存在;ali-oss/mysql2 是唯一"功能面大于消费面"的依赖对(§6),处置捆绑在模块去留决策上。真正需要盯的升级风险集中在两处——TypeORM 0.3.x 的下一个大版本(迁移体系若有破坏性变更,11 个迁移的可重放性需要重新验证)与 Shiki v1 锁定(安全通告若停止覆盖 v1 线,被迫升级时 highlighter.ts 要重写,建议提前立项)。bcryptjs v3 属于纯 JS 实现的小众版本线,注意与 @types/bcryptjs ^3.0.0 的配套。
版本纪律(A):全项目仅 3 处精确锁版(dayjs、vuedraggable、以及 echarts-stat 相关拆包规则隐含的稳定性假设),其余全部 ^——这与"pnpm-lock.yaml 冻结 + CI --frozen-lockfile"的组合是合理的双层结构:日常靠锁文件保证可重现,升级靠显式改 package.json 走评审。唯一建议补充的机制是 pnpm outdated 的季度例行化(或 Renovate 类工具),因为当前依赖面(尤其 Canvas 九库)的升级窗口完全依赖人工记忆,而 gsap 事故注释证明这个项目的依赖升级从来不"顺手"。
一条总建议:本项目依赖表的复杂度主要来自"内容清单驱动依赖"这一领域事实,任何"减依赖"的冲动都应先问"对应的演示内容是否还保留"——依赖是内容的影子,清理影子之前先清理本体。
本篇完。下一篇:18-代码走读实录.md