项目:html5-games | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
既有篇章零散标注过的「⚠️ 待确认」与风险点,本篇集中清点、逐条给出「位置 / 证据 / 影响 / 建议」四要素,并在此基础上给出一份分阶段的演进路线图。先定基调:这个项目的债务总量小、烈度低——它是单人维护的纯前端静态站,没有架构级腐化的土壤;台账里多数条目属于「注释漂移、死字段、历史命名」这类惰性债务,真正值得投入的是门禁缺口(E-10)与游戏基建渗透率(E-09),因为它们决定了未来债务的增长速度。
| 编号 | 债务 | 类型 | 烈度 | 建议时机 |
|---|---|---|---|---|
| E-01 | 「扩至 80 款」注释与 79 实际数漂移 | 注释漂移 | 低 | 顺手修 |
| E-02 | LocalGame.engine/library 死字段 | 死代码 | 低 | 下次动 config 时 |
| E-03 | difficulty 字段全空且无消费方 | 死字段 | 低 | 与 E-02 同批决策 |
| E-04 | 8 处存档短键偏差 + 1 处动态键 | 历史命名 | 中 | 做「纪录中心」前必解 |
| E-05 | 游戏层 74/79 为 JS SFC,类型覆盖缺口 | 分层策略 | 中 | 增量推进 |
| E-06 | gobang 越界音效名('lose'/'draw' 无声) | 潜伏 bug | 低-中 | 尽快修(10 分钟) |
| E-07 | useGameBgm.test 用例名「80 款」与 8 个入参的措辞漂移 | 注释漂移 | 低 | 顺手修 |
| E-08 | usePagination 注释引用不存在的 xiaoye-ui 依赖 | 注释漂移 | 低 | 顺手修 |
| E-09 | 画布/循环基建渗透率低(仅 4 款消费 useCanvasStage) | 架构债 | 中 | 随游戏迭代逐步 |
| E-10 | 门禁缺口:音效名/键名契约无测试、无格式检查、无依赖审计 | 流程债 | 中 | 尽快(见 09 篇 P0/P1) |
位置:src/config/games.ts 文件头第 9 行。证据:注释写「再经『第三批精选集成』扩至 80 款」,而 localGames 数组实际 79 条(games.test.ts 用例 1 断言 79,本套文档 00 篇第 4 节的统计口径说明已裁定「以测试断言为准」)。推断成因:第三批集成时配置曾达 80 条,随后某条目连同文件被删除(sortOrder 0-79 区间缺 1,疑似该批次某款下线的痕迹),注释未回改。影响:新接手者按注释理解规模会产生 80/79 的困惑,00 篇不得不专门写一段口径说明;更隐蔽的危害是「注释与测试矛盾」会动摇「注释可信」的默认预期。建议:把注释改为「……扩至 80 条配置(后 1 款下线,现 79 条)」或在行尾追加「(当前 79)」;若能从 git log 找到下线条目,注明 id 更佳。工作量一行。
位置:src/config/games.ts 20-23 行(接口定义)与全部 79 条目(engine: 'game'、library: 'showcase' 各出现 79 次);连带 localGameFileName(id)(1098-1100 行)。证据:注释自述「固定为 game,供详情页 VisualDemo 类型消费」「固定为 showcase,供详情页 VisualDemo 类型消费」,但仓库内已无 VisualDemo 类型,全项目 grep 无任何对这两个字段、对 localGameFileName 的运行时读取(《03-接口设计》1.1 早已标注 ⚠️ 待确认,本条为正式裁定)。影响:每新增一款游戏都要照抄两个无语义的常量(06 篇 SOP 第 2 步也在延续这一仪式),79×2 的无效代码与认知税;字段语义失真(详情页真正消费的是 id/category/title/description/group)。建议:清理需一次到位——删除接口两个字段、删除 79 条目中的赋值、删除 localGameFileName(或让它被键名工具真正消费,见 E-04),同时更新 06 篇 SOP 与 03 篇接口文档;清理前先确认主项目 html5-examples 不再与本仓库共享该类型(见《01-系统架构》第 6 节的两项目关系)。这是台账中唯一「越拖越贵」的条目(每新增一款游戏都在复制债务),优先级应高于其烈度所示。
位置:src/config/games.ts 接口 29 行与全部条目(均为 difficulty: '')。证据:79 条无一例外为空串;UI 层(GamesPage/GameCard/GameDetailPage/GameShell)无任何读取;测试未对它断言。影响:接口面上多一个「看起来有意义」的字段,接手者可能误以为它驱动某种难度分级;与 E-02 同属登记仪式的一部分。建议两条路线二选一:(a) 删除——与 E-02 同批清理,接口减一行;(b) 激活——若未来想做「难度筛选」,为 79 款补值(该工作量不小且主观),并在 GameCard/筛选管道消费。在没有产品需求前建议 (a);若担心未来回加,保留字段但在注释写明「未消费,历史预留」。
位置与证据(键名 → 文件,grep 复核):h5g:snake-best(snake.vue)、h5g:fifteen-puzzle-best(fifteen-puzzle.vue)、h5g:shot-ball-physics-best(shot-ball-physics.vue)、h5g:special-look-def-color-best(special-look-def-color.vue)、h5g:special-man-down100-best(special-man-down100.vue)、h5g:whack-a-mole-best(whack-a-mole-game.vue,键比 id 还短,是唯一的真键名漂移)、h5g:video-puzzle-best(video-puzzle.vue)、h5g:pixi-invaders-best(pixi-invaders.vue);此外 memory-match-game.vue 第 42 行使用动态键 h5g:memory-match-best-${size.value}(按 4×4/4×6 分键)。影响已充分论证(《04-数据模型》2.3):对内零成本——各游戏把键写在常量里,没有「从 id 推导键」的代码路径;对外则成为「按 id 枚举键」类功能(全站纪录中心、数据导出、清孤儿键工具)的硬阻塞。建议采用《11-扩展开发指南》场景五给出的双读映射而非批量改名:新增 bestKeyOf(id) 并内置 8 项豁免表,规范键直出、历史键查表;同时把 E-02 中待删的 localGameFileName 语义合并进来(「id → 文件名」「id → 存档键」两张映射表放同一文件,注释互相引用)。禁止的动作:改名旧键(老玩家纪录感知性清零,04 篇已裁定不迁移);模仿短键新增键(用 09 篇 P0 的键名契约测试拦住)。
位置与证据:src/games/ 79 个游戏组件中仅 5 款使用 lang="ts"(huarongdao-game / lianliankan / match3-game / sokoban-game / sudoku-game,grep 复核),其余 74 款为 JS SFC——vue-tsc 对它们不检查内部,@typescript-eslint 的类型规则也不生效,lint 只提供语法层与 Vue essential 规则(《09-测试与质量保障》第 3 节的捕捉半径分析)。影响:游戏层接口违约(如 E-06 的越界枚举、忘清理监听器)没有编译期防线,只能靠 review、运行时兜底(onErrorCaptured)与线上现象发现;这是「框架强类型、游戏弱类型」分层策略(《01-系统架构》5.2)的代价面。建议采用 01 篇已预告的增量策略并具体化:新游戏必须 TS(写入 06 篇 SOP 的修订版);旧游戏触及即改(改哪款补哪款的类型,避免一次性 74 款的无效 diff);试点样板——下一个用 Phaser 的新游戏直接 lang="ts"(见《11-扩展开发指南》场景四),为社区贡献者提供范式;不追求完成度指标,接受双轨长期并存,把目标定为「TS 游戏数单调递增」。同时把 E-06 这类「JS 层逃逸」风险用扫描式测试(09 篇 P0)补上,弥补类型缺位期间的保护。
位置与证据:src/games/gobang.vue 250 行 play('lose')、253 行 play('draw')、257 行 play(w === 3 ? 'draw' : 'win')——而 useGameAudio.ts 的 GameSfx 联合类型(11-27 行)与 render() switch(105-165 行)均无 'lose'/'draw',未知名落入 switch 无 case 分支静默无声。影响:五子棋人机模式输棋与和棋、双人模式平局时无反馈音(endGame 的其他分支有声),用户可感知的体验瑕疵;更重要的是它证明「JS 层可以绕过枚举契约」这一机制性风险(E-05 的实证)。建议:修复本身二选一——把 'lose'/'draw' 正式加入 GameSfx 与 render(新增两个失败/平局音色,注意 16 个现有音色中没有「失败下行但区别于 gameover」的现成选择),或把 gobang 的调用改为就近音色('gameover'/'win',一行改动);配套在 games.test.ts 或新测试文件加音效名扫描断言(09 篇 P0),防止同类回归。作为 bug 修复走正常提交流程即可,无需专门排期。
E-07 位置:src/composables/__tests__/useGameBgm.test.ts 最后一个用例名「80 款游戏标题至少分化出 4 种速度与 6 首不同曲子」——断言入参实际只有 8 个标题(58 行),「80 款」是写作时的规模口径残留,与 E-01 同源(80 注释时代)。影响:用例名与断言内容不符,削弱测试作为文档的可信度。建议改为「代表性游戏标题……」或直接列数「8 个标题」。E-08 位置:src/composables/usePagination.ts 33-34 行注释「UI 组件使用 src/components/layout/Pagination.vue(内部基于 xiaoye-ui Pagination,样式已覆盖为项目设计 token)」——而 Pagination.vue 是自研实现(无任何第三方 import,package.json 无 xiaoye-ui 依赖;同目录 AppButton/AppInput 的注释反而明确写着「不依赖 xiaoye-ui」)。影响:误导接手者以为存在 UI 库依赖,排查分页问题时可能去找不存在的组件源码。建议删除括号内引用或改为「自研实现,与主项目视觉语言一致」。两处均为一行注释修改,可与 E-01 合并为一次「注释卫生」提交。
位置与证据:useCanvasStage/useCanvas 全仓库仅 4 个游戏消费(grep 复核);useAnimationLoop 消费面略广但仍非主流;多数游戏在 onMounted 自管 getContext/DPR/rAF/事件注册(suika-game.vue、tetris 的 onMounted 与 plane-shooter 的 onMounted 是三种略有差异的自管写法)。影响:清理与后台暂停质量参差——自管写法若漏掉 pauseWhenHidden 或清理配对,表现为后台功耗与慢性卡顿;同时 79 款中同一模式有三份以上变体实现,修复一处 bug(如 dt clamp 边界)需要逐款排查而不是改一处基建。建议:不做批量重构(风险收益比差,与 E-05 同理),执行「触及即迁移」——任何一款游戏需要实质性修改时,顺手迁到 useCanvasStage;新游戏 SOP 直接规定使用基建;把「pauseWhenHidden 是否开启」加入 06 篇手工验收清单。配套动作是把《07-模块详解》第 5 节指出的 useCanvas 旧别名在文档中标注「新代码勿用」,防止渗透率进一步碎片化。
位置与证据:见《09-测试与质量保障》第 4 节的 P0-P3 全表。摘要:音效名与存档键名两类契约无扫描测试(导致 E-06 逃逸到线上)、Prettier 格式不进 CI、无依赖审计、useListFilter 无测试、registry 与测试的 glob 双份。影响:这是唯一随时间主动增长的债务源——每新增一款游戏、每发一版都在无守护的面上行走。建议按 09 篇优先级执行,P0 两条(两个扫描式测试)合计半天工作量,是全台账性价比最高的投入;P1 的 prettier --check 与 Dependabot 各一行配置。本条不展开,以 09 篇为准。
把台账与各篇的建议汇成三个阶段,阶段间的门禁是「前一阶段的关键交付完成且五连绿」。
第一阶段:卫生与守护(1-2 天,可拆散进日常)。 修 E-06(音效名)与三处注释漂移(E-01/E-07/E-08)——一次「债务卫生」提交;落地 09 篇 P0 的两个扫描式测试(音效名、键名豁免清单)与 P1 的 prettier --check、Dependabot——门禁从「五关」升级为「五关 + 两契约 + 两流程」。此阶段零行为变更、零风险,但把 E-10 的「主动增长」止住。验收标准:CI 新增步骤全绿;gobang 音效可闻;测试红路径用临时越界样例验证过。
第二阶段:结构减负(1 周内的一次专项)。 执行 E-02/E-03 的死字段清理(engine/library/difficulty/localGameFileName 四项联动,同步修订 03/06 篇文档与 SOP);落地 E-04 的 bestKeyOf 双读映射与键名豁免表;为 useListFilter 补测试(09 篇 P1)。此阶段全部是「删代码 + 加工具」,不改任何运行时行为(键映射必须通过「双读结果与原常量一致」的对照测试验证)。验收标准:LocalGame 接口收敛到 7 个字段;新测试覆盖映射表全部 79 id;文档三处同步。
第三阶段:按需演进(以需求为触发,无固定排期)。 四个候选方向按触发条件挂起:(a) 游戏层 TS 化(E-05)——以新游戏强制的静态策略持续滚动,无专项;(b) 全站纪录中心——触发条件是产品确有聚合展示需求,落地时消费第二阶段的 bestKeyOf(《11-扩展开发指南》场景五已给 SOP);(c) 进度存档(续玩)——触发条件是出现长局游戏需求,按 h5g:<id>-save 新键 + 版本化快照设计(《04-数据模型》2.3 预告);(d) 接入引擎试点——触发条件是某玩法确实超出原生 Canvas 的舒适区,按《11-扩展开发指南》场景四执行并允许其成为 TS 游戏样板。两个远期选项明确不推荐:引入后端/排行榜(动摇「纯静态可部署」与「无隐私边界」两大卖点,见《08-安全与权限设计》第 5 节的重新设计警告);批量重写 74 款 JS 游戏(无效 diff 风暴,违反增量纪律)。
路线图的收束原则一句话:这个项目的债务特征是「轻、惰、散」,治理它的正确姿势不是大扫除而是「守护住增长、清理顺手做、重构等触发」——三阶段各司其职,任何一个接手者都能在第一周内完成第一阶段的全部动作。