项目:html5-games | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
本篇对 package.json 的全部依赖做逐个解析。先给全量结论:这是一个依赖面极窄的项目——dependencies 仅 5 项(vue、vue-router、@vueuse/core、pixi.js、material-symbols),devDependencies 14 项(构建 4 项、类型 2 项、测试 2 项、代码质量 6 项),没有任何状态库、UI 组件库、存储库、工具 lodash、HTTP 客户端。运行时依赖的全部体积大头是 pixi.js,且被 pixi-vendor 隔离为按需 chunk(见《16-决策记录ADR》ADR-009)。版本信息来自两处:package.json 的语义化范围(^ 前缀)与 pnpm-lock.yaml 的锁定版本(本文标注为「锁定」);下文引用的锁定版本以当前锁文件为准。与《14-配置与环境详解》的分工:那篇逐段解读配置文件本身(scripts、tsconfig、eslint 配置语义),本篇回答「每个包是什么、为什么在、在哪里被用、升级会碰到什么」。
| 依赖 | 版本(范围/锁定) | 用途 | 使用位置 | 选型理由 | 替代方案(未被选) | 升级风险 |
|---|---|---|---|---|---|---|
| vue | ^3.5.0 / 3.5.42 | UI 框架:Composition API + <script setup>,承载全部组件 | 全项目(main.ts、App.vue、2 个页面、约 30 个框架组件、79 个游戏 SFC) | defineAsyncComponent 天然衔接 glob 懒加载;单文件组件使每款游戏自包含成文件 | Svelte/Solid(生态与团队惯性)、原生 Web Components(游戏内状态管理过繁) | 中:渲染器内部 API 变动影响 defineAsyncComponent 行为的场景罕见;关注 minor 的 deprecation 警告 |
| vue-router | ^4.3.0 / 4.6.4 | SPA 路由(仅 2 条业务路由 + 2 条 redirect) | src/router/index.ts、App.vue、GamesPage、GameDetailPage、GameCard | 深链(/game/:id)与 scrollBehavior 恢复是硬需求;懒加载路由组件与代码分割天然集成 | hash 路由自研(失去嵌套/守卫生态)、无路由(手写 history 管理,深链与滚动恢复全要自建) | 低:本项目只用到路由表、meta、守卫、onError 等最稳定面 |
| @vueuse/core | ^14.3.0 / 14.4.0 | 组合式工具库;实际仅消费 watchDebounced 一个函数 | src/composables/useListFilter.ts 第 2 行(唯一 import 点) | 搜索防抖是列表页核心体验,watchDebounced 实现成熟且被测试覆盖的边界多 | 自写 300ms debounce watch(可行,约 15 行,但要处理边界:立即回调、unmount 竞态、SSR——本项目无 SSR,自写成本真实存在但不大) | 低:单函数依赖,tree-shaking 后增量极小;大版本升级需复核该函数签名 |
| pixi.js | ^8.19.0 / 8.20.1 | WebGL 2D 渲染引擎 | 仅 3 款游戏:pixi-fireworks.vue / pixi-pong.vue / pixi-invaders.vue | 粒子烟花与弹球对战需要批量绘制与渲染管线性能;重写为 Canvas 2D 是能力倒退 | Phaser(1MB+,对 3 款轻玩法过重,见《11-扩展开发指南》场景四)、原生 Canvas(粒子规模受限) | 中-高:pixi 8 的 major 间 API 断裂史真实存在;锁定在 minor 内更新即可,且体积被 pixi-vendor 隔离(普通玩家不下载) |
| material-symbols | ^0.45.8 / 0.45.10 | 图标字体(自托管 npm 包,Google Material Symbols 的离线分发) | src/main.ts 第 5 行 import 'material-symbols/outlined.css',经 AppIcon.vue 消费 | 无在线图标 API 依赖;npm 版本化、离线可用 | 在线 Material Symbols CDN(外部请求+不稳定)、SVG 图标集手动管理(更新烦) | 低-中:实测事故在案——个别连字缺失(casual 分类 ms:catching_pokemon 以字面文本渲染撑破侧栏,games.ts 68-69 行注释「已实测」),升级图标字体后必须全站图标目检 |
五个运行时依赖里最值得注意的是 @vueuse/core 的「单函数消费」形态:全项目唯一 import 是 useListFilter.ts 的 watchDebounced,该文件第 30 行注释还提到 useUrlSearchParams——但 grep 全仓库并无第二处 @vueuse import,useUrlSearchParams 仅出现在注释里未被使用 ⚠️ 待确认(疑似写注释时的方案描述残留,见《13-技术债务与演进建议》对注释漂移的处理惯例 E-07/E-08 同类)。这说明该依赖处于「用了很小、但离开了也不痛」的状态:它买的不是体积,是防抖边界的成熟度。
明确不存在的依赖(与《00-项目总览》第 3 节「明确不引入的东西」对齐):无 Pinia/Vuex(状态靠模块级单例,ADR-006)、无 UI 组件库(自研薄封装,ADR-011)、无 idb / localForage 等存储工具库(存储就是裸 localStorage + src/utils/highScore.ts 两个函数 + 各游戏内联 try/catch,见《04-数据模型》第 2 节——有读者会预期这类项目用 IndexedDB 库,实际没有,这是 ADR-007 的刻意决策)、无 lodash/ramda(工具函数自写,且几乎没有需要的地方)、无 axios/fetch 封装(无后端,唯一外部请求是字体与 Umami 统计脚本,都在 index.html 里声明,不经 JS 运行时)。
vue 是全项目唯一的「结构性」依赖:79 个游戏 SFC、约 30 个框架组件、2 个页面全部构建在它之上。本项目的 vue 用法有三个特征值得记录。其一,API 面收敛:全项目几乎只用 Composition API 的核心十几个导出(ref/computed/watch/defineAsyncComponent/onMounted 系),未用 vuex 系概念、未用 render 函数(唯一例外是 GameDetailPage.vue 内联的 GameLoading 占位组件用了 defineComponent + h(),第 21-27 行)、未用 Teleport/Suspense。其二,响应式用法有明确纪律:游戏高频状态放普通变量、UI 镜像才用 ref(见《04-数据模型》第 3 节模式二),ctxRef 用 shallowRef 避免对宿主大对象做深度代理(《07-模块详解》第 5 节)——这些纪律让 vue 的响应式开销对 60fps 游戏循环不构成威胁。其三,异步组件是加载框架的支点:defineAsyncComponent 的 loadingComponent/delay 参数被详情页用于防闪烁 spinner(GameDetailPage.vue 55-57 行 delay: 100),与 registry 的 chunk 映射严丝合缝。
vue-router 的存在感更薄但作用关键:路由表就 4 条(games、game/:id、两条 redirect,见 src/router/index.ts),项目真正消费的能力是——meta.hideHeader 驱动 App.vue 隐藏顶栏让游戏整屏、scrollBehavior 与 rememberLobbyScroll 配合恢复大厅滚动位置(模块级变量,第 22-26 行)、router.onError 检测「Failed to fetch dynamically imported module」后 window.location.assign 整页刷新自愈(第 47-53 行,静态站发版后旧 hash chunk 404 的兜底)、beforeEach 设置文档标题。升级风险总体低;唯一要留意的细节是 scrollBehavior 返回 { behavior: 'instant' } 的注释依据:「全局 smooth 会让恢复动画依赖帧产出,在按需出帧环境里迟迟不到位」(第 30-31 行注释)——这是对 router 内部行为敏感的用法,router 大版本升级时应回归验证返回大厅的滚动恢复。
pixi.js 是全项目体积最大、耦合面却最小的运行时依赖。耦合面小:仅 3 个游戏 import 它(grep from 'pixi.js' 精确命中 3 个文件),框架层、其他 76 款游戏对它零感知;体积大:锁定版本 8.20.1,压缩后约 488K(对比见《13-技术债务与演进建议》:Phaser 3 约 1MB+,这是当初选 pixi 而非 Phaser 的量化理由之一)。vite.config.ts 的 manualChunks 把它隔离为 pixi-vendor,只有打开 3 款 pixi 游戏时才会下载——这让「重依赖」没有转嫁为「全站税」。
使用层面的三个约束已在既有篇章确立,此处汇总为依赖视角的备忘:约束一,init 异步竞态——Application.init() 是异步的,组件可能在 init 完成前被卸载,pixi-pong.vue 以 destroyed 标志位防护(第 34 行声明、370-395 行检查与销毁),任何新的 pixi 游戏必须复制此模式(《03-接口设计》第 2 节契约注记)。约束二,销毁语义——卸载钩子里必须 app.destroy(true) 释放 WebGL 上下文,否则反复进出游戏会累积上下文(浏览器对 WebGL 上下文数量有上限)。约束三,升级策略——pixi 8 相对 7 已是一次全量 API 重写(async init、Container 体系变更),major 升级等于重写 3 款游戏;务实策略是锁定 ^8.19.0 范围内的 minor/patch,major 升级挂起至「3 款游戏确需新特性」时再评估。当前 lock 停在 8.20.1,与 8.x 系列兼容 ⚠️ 待确认(pixi 后续 minor 是否有 deprecation——建议升级前跑一遍 3 款游戏的手工回归)。
这个依赖的价值不在功能而在它的使用方式记录了项目的工程态度。选择它是为了「图标即文本」:AppIcon.vue 以 ms:xxx 名字消费连字(ligature),字体文件自托管于 node_modules(main.ts 第 5 行引入),构建时随 CSS 分发,无运行时外部请求。代价是字体方案的固有风险——连字依赖字形覆盖,缺了就以字面文本渲染。项目用一段源码注释把实测结果固化成知识:games.ts 68-69 行「不可用 ms:catching_pokemon——该连字在 material-symbols@0.45.8 字体中缺失,会以字面文本渲染并撑破侧栏(已实测),此处选用语义相近的 toys」。这实际上建立了一条隐式规范:升级 material-symbols 必须全站图标目检(尤其分类图标这种布局敏感位置);锁定在 0.45.10 的 patch 级更新相对安全(同一字体族的补字),minor 跨度升级则要复测。
在「依赖全景」里,不存在的依赖与存在的依赖同样值得解释,因为它们都是决策的产物。项目存储需求是:79 个最高分标量键、4 个复合 JSON 键、2 个偏好布尔键(全景见《04-数据模型》2.1)。这些数据的特征:单值极小、总量极小、同步读写、无查询需求、无容量压力。localStorage 完全覆盖,而 IndexedDB 的异步 API 反而会把每个读写点变成 Promise 链,同步的「游戏结束即写档」逻辑会因此复杂化。于是存储层被压缩为:src/utils/highScore.ts(23 行,readHighScore/writeHighScore)+ 各游戏内联的 try/catch 范式(gobang.vue 115-132 行 readRec/saveRec 是标准样板)+ 两个 composable 各自的偏好读写。没有存储依赖不是疏漏,是 ADR-007「分层兜底、fire-and-forget」决策的依赖面体现。若未来出现「进度存档(续玩)」(结构化快照、可能超 5MB)或「全站纪录中心」(枚举键 + 查询),按《13-技术债务与演进建议》第三阶段的路线,届时才重新评估是否引入 idb——在那之前,引入它只会增加抽象层而不会增加能力。
| 依赖 | 版本(范围/锁定) | 用途 | 使用位置 | 选型理由 | 替代方案(未被选) | 升级风险 |
|---|---|---|---|---|---|---|
| vite | ^5.0.0 / 5.4.21 | 构建工具:dev server、生产构建、代码分割 | vite.config.ts;import.meta.glob 是加载框架基石 | glob 语义化 import 是注册表方案的前提;esbuild 预构建速度 | webpack(glob 需 require.context,历史 API)、Rspack/turbopack(生态成熟度当时不如 vite 5) | 中:Vite 6/7 的 Environment API 与 Rolldown 迁移是近年最大变数;升级须回归 manualChunks 与 glob key 格式(/src 前缀) |
| @vitejs/plugin-vue | ^5.0.0(锁定与 vite 5 配套) | SFC 编译插件 | vite.config.ts 第 2 行 | 官方插件,与 vite 版本线配套 | 无实际替代 | 低:跟 vite 大版本联动升级即可 |
| @tailwindcss/vite + tailwindcss | ^4.0.0 / 4.3.3 | 原子化 CSS;v4 的 @theme inline 桥接 CSS 变量令牌 | vite.config.ts 插件、src/assets/main.css、模板内工具类 | v4 无配置文件、以 CSS 为中心,与项目的「设计令牌 + CSS 变量」体系同构 | Tailwind v3(需 tailwind.config.js,双份事实)、UnoCSS(团队无既有范式) | 中:v4 相对 v3 是范式变更;已过迁移期,风险在 v4→v5 的未来 |
| typescript | ^5.0.0 / 5.9.3 | 框架层类型系统(strict) | tsconfig.json、vue-tsc、eslint-parser | 事实标准 | 无 | 低:strict 面固定,minor 升级偶发新检查项报错,属可控 |
| vue-tsc | ^3.3.8 / 3.3.11 | SFC 类型检查(pnpm typecheck = vue-tsc --noEmit) | package.json scripts | vue 官方类型检查器 | 无 | 低-中:与 vue/typescript 版本联动 |
| vitest | ^2.1.9 / 2.1.9 | 单元测试 | vitest.config.ts(node 环境、src/**/__tests__/**/*.test.ts)、5 个测试文件 | 与 vite 同源配置(复用别名),API 兼容 jest 子集 | jest(配置分裂,需另接 vite 转译) | 低:测试面小(4 个文件 24 用例左右,见《09-测试与质量保障》) |
| eslint + @eslint/js | ^10.4.1 / 10.10.0(@eslint/js ^10.0.1) | 代码质量门禁 | eslint.config.js(平面配置) | flat config 单文件、与 prettier 兼容层清晰 | biome(快但规则面不全) | 低 |
| eslint-plugin-vue | ^10.9.2 | Vue 规则(flat/essential) | eslint.config.js | 官方插件 | 无 | 低 |
| typescript-eslint | ^8.61.0 | TS 解析与规则(recommended) | eslint.config.js | 官方合并包 | 无 | 低 |
| eslint-config-prettier | ^10.1.8 | 关闭与 prettier 冲突的规则 | eslint.config.js 末位 | 标准做法 | 手工排冲突(不可维护) | 低 |
| prettier | ^3.5.3 | 格式化(pnpm format;CI 未跑 check,见《09-测试与质量保障》第 4 节缺口) | scripts.format | 事实标准 | dprint | 低 |
| globals | ^17.11.0 | 浏览器全局变量清单(eslint 配置用) | eslint.config.js | 配套件 | 无 | 低 |
开发依赖的分组逻辑一目了然:构建线(vite + plugin-vue + tailwind 系)决定产物形态;类型线(typescript + vue-tsc)是「框架层 strict」的物质基础;测试线(vitest)单文件配置;质量线(eslint 四件套 + prettier)构成 CI 五关中的 lint 关。各线内部版本耦合明确:plugin-vue 跟 vite、vue-tsc 跟 typescript、vitest 跟 vite——升级时按「线」整体推进而不是逐包打散。值得重复的一个门禁事实:prettier 只有 format 脚本没有 --check,格式一致性目前靠自觉,这与《13-技术债务与演进建议》E-10「门禁缺口」的裁定一致(P1 建议加 prettier --check 进 CI)。
包管理与版本策略由三个字段钉住(package.json):"packageManager": "pnpm@10.23.0"(Corepack 据此自动取版,保证所有人用同一 pnpm)、"engines": { "node": ">=22" }(运行时下限)、"type": "module"(全 ESM,与 vite/eslint 平面配置的 ESM 形态一致)。语义化范围统一用 ^(锁 minor 内更新),实际安装版本以 pnpm-lock.yaml 为准——本篇引用的锁定版本即出自该文件。项目没有 Dependabot/Renovate 配置 ⚠️ 待确认(《13-技术债务与演进建议》E-10 把「无依赖审计」列入缺口,P1 建议是一行配置);在补上之前,升级惯例是按需触发而非定期跟进:某依赖有安全通告、或某特性需要新版本时才动,动后跑「提交前五连」(pnpm typecheck && pnpm lint && pnpm test && pnpm build,见《09-测试与质量保障》第 3 节)。
升级敏感度排序(高→低):pixi.js(major 即重写)> vite(manualChunks/glob key 是两处硬耦合)> tailwindcss(范式级)> vue / vue-tsc / typescript(稳定面大但联动)> material-symbols(连字目检)> vue-router / @vueuse / vitest / eslint 系(低)。这个排序也回答了「如果想主动升级,先动谁」:从低敏感度开始建立升级肌肉记忆,pixi 与 vite 放到有完整回归时间的窗口。
总量维度:优。 运行时 5 项、开发期 14 项,对一个 79 游戏规模的站点属于显著低位;无重复功能的依赖(没有两个状态库、两个日期库之类的常见病)。传递依赖经 pnpm 严格隔离(pnpm 的符号链接结构天然防幻影依赖),pnpm-lock.yaml 提交入库保证可复现。
安全维度:无已知高危,但无持续审计。 运行时依赖没有处理不可信输入的代码面(vue/pixi 的 CVE 历史多集中于 SSR 或特定 API),index.html 的两个外部引用(Google Fonts、Umami 统计)是仅有的外部信任面,已在《08-安全与权限设计》第 3 节单独评估。缺审计自动化(Dependabot 未配)是唯一扣分项,修复成本一行配置。
时效维度:健康中带两处「中风险钉子」。 主力版本线都是各自生态的现行主线(vue 3.5、vite 5、tailwind 4、ts 5.9、eslint 10)。两处需要留意的钉子:vite 5 在 vite 7 时代已非最新 major,升级时 manualChunks 函数与 import.meta.glob 的 key 格式(/src 前缀)是必须回归的两点(ADR-001/ADR-009 的后果面);pixi 8 的 major 跃迁成本高,建议锁定并挂起。
耦合维度:优。 重依赖全部有隔离或收敛措施——pixi 有 pixi-vendor 且仅 3 文件触碰、@vueuse 仅 1 函数 1 文件、material-symbols 经 AppIcon 单点消费。这意味着任何一项运行时依赖的移除/替换成本都是「天」级而不是「周」级,依赖没有变成架构。
结论:依赖健康度评级 A-(扣分项:无审计自动化、vite/pixi 两个中风险钉子缺升级演练)。对这个项目的接手者,本篇的可执行清单只有三条:保持「按需升级 + 五连验证」的现行节奏;补一行 Dependabot(或手动每季度跑一次 pnpm outdated 过目);升级 material-symbols 或 pixi 前先读本篇 2.2/2.3 的两条回归清单。