279 lines
13 KiB
Markdown
279 lines
13 KiB
Markdown
# 工程优化审查报告(2026-03-29)
|
||
|
||
## 说明
|
||
|
||
- 扫描范围:`src/`、`scripts/`、`docs/`、`package.json`、`vite.config.ts`、`tsconfig.json`
|
||
- 已执行校验:`npm run lint`、`npm run build`、`npm run check:content`
|
||
- 本报告只从工程角度讨论结构、边界、质量门禁、可维护性与可扩展性
|
||
- 按仓库说明,暂不讨论中文乱码本身
|
||
|
||
## 当前结论
|
||
|
||
项目当前**可构建、可运行、内容校验可通过**,说明基础功能链路是通的;但从工程视角看,已经出现明显的“单点过重、边界混杂、质量门禁偏弱、编辑器与运行时耦合”问题。继续叠需求会越来越依赖人工记忆和局部经验,回归风险会持续上升。
|
||
|
||
当前最值得优先处理的不是单个 UI 细节,而是以下四个工程主题:
|
||
|
||
1. 运行时主链路的职责拆分还不够,核心 hook / 组件已经过载
|
||
2. 缺少真正的工程质量门禁,`lint` 目前本质上只是 `tsc`
|
||
3. 编辑器、运行时、类后端能力都混在同一个 Vite 配置里
|
||
4. 持久化、AI 调用、编辑器保存等基础设施仍然是“分散手写”
|
||
|
||
## 运行状态快照
|
||
|
||
- `npm run lint` 通过
|
||
- `npm run build` 通过
|
||
- `npm run check:content` 通过
|
||
- 应用代码下未发现测试文件:`src/`、`scripts/`、`docs/` 内没有 `*.test.*` / `*.spec.*`
|
||
- 构建产物已出现较大 chunk
|
||
- `dist/assets/App-*.js` 约 `407 KB`
|
||
- `dist/assets/itemCatalog-*.js` 约 `414 KB`
|
||
- `dist/assets/PresetEditor-*.js` 约 `109 KB`
|
||
|
||
## 代码体征
|
||
|
||
下列文件已经明显进入“超大模块”区间:
|
||
|
||
| 文件 | 行数 | 观察 |
|
||
| --- | ---: | --- |
|
||
| `src/hooks/useStoryGeneration.ts` | 3304 | 同时管理剧情、NPC 交互、交易、送礼、招募、任务、角色聊天、道具/锻造接入 |
|
||
| `src/components/PresetEditor.tsx` | 2244 | 多编辑器入口聚合在一个巨型组件中 |
|
||
| `src/hooks/useCombatFlow.ts` | 1791 | 同时承担战斗推演、动画时序、逃跑演出、状态落地 |
|
||
| `src/components/GameShell.tsx` | 1592 | 入口 UI、选角、世界选择、自定义世界、场景切换、浮层控制全部集中 |
|
||
| `src/types.ts` | 663 | 运行时、AI、编辑器、自定义世界、背包、任务类型集中在一个总文件 |
|
||
|
||
补充信号:
|
||
|
||
- `src/components/GameShell.tsx` 内有 16 个 `useState`、10 个 `useEffect`、13 个 `useMemo`
|
||
- `src/hooks/useStoryGeneration.ts` 虽然只有少量 React state,但内部累计 40+ 个函数,已经是“巨型流程控制器”
|
||
- `src/hooks/useCombatFlow.ts` 内有大量时间常量、动画常量、`sleep + setGameState` 过程式循环,测试成本很高
|
||
|
||
## 优先级问题
|
||
|
||
## P0:运行时主链路职责过度集中
|
||
|
||
证据:
|
||
|
||
- `src/hooks/useStoryGeneration.ts:868-930` 进入 hook 后立即开始定义交易、送礼、招募、角色聊天等子流程
|
||
- `src/hooks/useStoryGeneration.ts:3191-3303` 返回对象同时暴露剧情、任务、NPC UI、角色聊天 UI、背包/锻造 UI
|
||
- `src/components/GameShell.tsx:293-360` 组件 props 很多,内部 state 也很多,承担“壳层 + 流程 + 浮层 + 自定义世界生成 + 场景切换”
|
||
- `src/hooks/useCombatFlow.ts:559-1787` 将战斗计算和战斗演出揉在同一层里
|
||
|
||
影响:
|
||
|
||
- 任何一个新需求都容易同时碰到剧情、UI、战斗、背包、NPC 关系四五条链路
|
||
- 代码 review 很难聚焦,改动一处时往往需要脑内跟完整条大流程
|
||
- 单元测试难写,因为逻辑不是纯函数,而是大量闭包 + 过程式状态推进
|
||
- 长期会形成“只有熟悉历史上下文的人才能安全修改”的隐性门槛
|
||
|
||
建议:
|
||
|
||
- 将 `useStoryGeneration` 拆为“剧情推进”“NPC 交互”“角色聊天”“任务结算”“模态框控制”几个子域
|
||
- 将 `useCombatFlow` 拆成“纯战斗结算引擎”和“战斗播放适配层”
|
||
- 让 `GameShell` 回到壳层职责,只负责路由态、页面态、模态挂载与 props 编排
|
||
- 以“领域职责”拆分,而不是按“文件太长了随便切一刀”拆分
|
||
|
||
## P0:缺少真正的工程质量门禁
|
||
|
||
证据:
|
||
|
||
- `package.json:11` 的 `lint` 实际只有 `tsc --noEmit`
|
||
- `package.json` 中没有 `test`、`format`、`lint:fix` 等基础脚本
|
||
- 根目录未发现 `.eslintrc*`、`.prettierrc*`、`.editorconfig`
|
||
- 代码目录下没有测试文件
|
||
|
||
影响:
|
||
|
||
- 当前项目的“能过 lint”只代表类型没炸,不代表风格一致、依赖正确、Hooks 规则正确、死代码已清理
|
||
- 大型 hook / 大型组件的重构几乎没有自动回归保护
|
||
- 运行时行为、编辑器行为、AI fallback 行为主要依赖人工回归
|
||
|
||
建议:
|
||
|
||
- 补齐 ESLint、Prettier、EditorConfig,至少覆盖 React Hooks、import、unused code、复杂度基线
|
||
- 引入 Vitest,先覆盖纯数据层与纯规则层
|
||
- 为 `useCombatFlow`、`stateFunctions`、`npcInteractions`、`questFlow` 增加单元测试
|
||
- 为“开局 -> 选世界 -> 选角色 -> 进入剧情 -> 战斗 -> 存档恢复”补最小 E2E smoke
|
||
- CI 中至少串联:类型检查 + 单测 + build + 内容校验
|
||
|
||
## P1:编辑器、运行时、类后端能力全部耦合在 Vite 配置里
|
||
|
||
证据:
|
||
|
||
- `vite.config.ts:151-203` 在 Vite 插件里实现了 LLM 代理
|
||
- `vite.config.ts:206-269` 在 Vite 插件里实现了通用 JSON 文件读写 API
|
||
- `vite.config.ts:253` 直接写回 `src/data/*.json`
|
||
- `vite.config.ts:265-266` 和 `vite.config.ts:400-401` 在 `preview` 阶段也挂了这些接口
|
||
- `vite.config.ts:425-434` 启动时默认把这些“编辑器后端能力”全部注册进去
|
||
|
||
影响:
|
||
|
||
- 本地编辑器能力与运行时能力没有清晰边界
|
||
- `preview` 环境仍可写源码文件,发布边界不清晰
|
||
- 未来如果要做独立部署、多人协作、远程编辑、权限控制,会非常难迁移
|
||
- Vite 配置同时扮演构建配置、代理层、文件服务层、编辑器后端,职责失衡
|
||
|
||
建议:
|
||
|
||
- 将编辑器读写 API 从 `vite.config.ts` 抽到独立的本地工具服务或独立脚本
|
||
- 至少区分 `dev-only write api` 与 `preview/prod read-only api`
|
||
- 对编辑器保存接口建立统一客户端 SDK,避免组件直接散落 `fetch('/api/...')`
|
||
- LLM 代理也建议独立成 `server/` 或 `scripts/dev-server/`,不要继续长在构建配置里
|
||
|
||
## P1:持久化策略分散,且直接序列化大状态对象
|
||
|
||
证据:
|
||
|
||
- `src/hooks/useGamePersistence.ts:152-167` 会在状态变化时自动把完整快照写入 `localStorage`
|
||
- `src/hooks/useGamePersistence.ts:157-163` 快照包含 `gameState + bottomTab + currentStory`
|
||
- `src/hooks/useGamePersistence.ts:68-116` 恢复逻辑已经开始承担大量 schema 纠偏职责
|
||
- `src/data/customWorldLibrary.ts:1-282` 自定义世界库单独维护一套 `localStorage` 读写与 normalize
|
||
- `src/hooks/useGameSettings.ts` 也单独维护一套本地设置持久化
|
||
|
||
影响:
|
||
|
||
- 状态结构一旦继续膨胀,快照写入频率和反序列化成本都会增加
|
||
- schema 迁移会越来越依赖手工 normalize 补丁
|
||
- 不同持久化入口各写一套 parser / normalizer,风格和鲁棒性难统一
|
||
- 当前保存的是“运行中大对象”,而不是“稳定领域快照”,长期会放大兼容成本
|
||
|
||
建议:
|
||
|
||
- 建立统一的 persistence 层,集中管理 key、version、migration、节流、序列化策略
|
||
- 对 `GameState` 做“可持久化切片”和“运行时临时切片”分层
|
||
- 自动保存增加节流/去抖,避免每次状态波动都全量落盘
|
||
- 如果继续扩展角色聊天、自定义世界、编辑器草稿,建议评估 IndexedDB 替代 `localStorage`
|
||
|
||
## P1:运行时与编辑器仍在同一个前端入口体系中,包体继续膨胀
|
||
|
||
证据:
|
||
|
||
- `src/main.tsx:21-34` 通过 `window.location.pathname` 手写分发页面
|
||
- `src/main.tsx:60` 只有“游戏”和“PresetEditor”两个大入口
|
||
- `PresetEditor`、`ItemCatalogEditor`、`StateFunctionEditor` 都属于重型模块
|
||
- 构建产物已经出现 `App` 约 `407 KB`、`itemCatalog` 约 `414 KB` 的 chunk
|
||
|
||
影响:
|
||
|
||
- 游戏端与编辑器端的演进节奏被绑定在一个 SPA 入口上
|
||
- 编辑器相关数据和静态资源容易继续抬高构建体积
|
||
- 未来增加更多编辑器页、更多世界模板、更多资源目录后,冷启动成本会更明显
|
||
|
||
建议:
|
||
|
||
- 将编辑器拆成独立入口,至少做成独立 route module,而不是单个 `PresetEditor`
|
||
- 继续下钻按 tab 做懒加载,尤其是 `items/functions/npcs`
|
||
- 将静态大数据、资源目录索引、编辑器专用预览逻辑做更细的 chunk 拆分
|
||
- 如果项目后续会长期保留编辑器,建议直接分成 game app / editor app 两个 entry
|
||
|
||
## P2:编辑器基础设施重复实现较多
|
||
|
||
证据:
|
||
|
||
- `src/components/PresetEditor.tsx:111-181` 自己实现 `cloneValue`、`saveJsonObject`
|
||
- `src/components/StateFunctionEditor.tsx:113-130` 再次实现 `cloneValue`、`SectionCard`
|
||
- `src/components/ItemCatalogEditor.tsx:94` 再次实现保存请求
|
||
- `src/hooks/useInventoryFlow.ts:8`、`src/hooks/useEquipmentFlow.ts:10`、`src/hooks/useForgeFlow.ts:12`、`src/hooks/useTreasureFlow.ts:10` 重复声明 `CommitGeneratedState`
|
||
|
||
影响:
|
||
|
||
- 修改保存行为、错误处理、深拷贝策略时需要多处同步
|
||
- 编辑器 UI 风格与交互行为容易逐步漂移
|
||
- 公共契约没有收拢到共享层,维护成本会逐步抬高
|
||
|
||
建议:
|
||
|
||
- 抽 `editor/shared/` 层,集中放保存 SDK、表单字段、卡片容器、克隆工具、错误处理
|
||
- 抽通用的 `CommitGeneratedState` 类型定义
|
||
- 将编辑器请求和覆盖保存逻辑统一走一个 client
|
||
|
||
## P2:类型系统已经出现“总文件过载”
|
||
|
||
证据:
|
||
|
||
- `src/types.ts` 共 663 行
|
||
- `src/types.ts:1-260` 同时包含世界、动画、技能、对话、自定义世界、物品等类型
|
||
- `src/types.ts:536-663` 又继续承接剧情、聊天、任务、`GameState`、AI 响应
|
||
|
||
影响:
|
||
|
||
- 任一领域类型变化都会增加总文件冲突概率
|
||
- 新人理解类型边界成本高
|
||
- 编辑器类型、运行时类型、AI 传输类型被放在一起,不利于演化
|
||
|
||
建议:
|
||
|
||
- 按领域拆分:`types/combat.ts`、`types/story.ts`、`types/item.ts`、`types/customWorld.ts`、`types/persistence.ts`
|
||
- `GameState` 相关类型与 editor override 类型分开
|
||
- AI request/response contract 单独收口,避免继续堆进总类型文件
|
||
|
||
## P2:AI 客户端层过厚,且重复了多套请求与解析逻辑
|
||
|
||
证据:
|
||
|
||
- `src/services/ai.ts` 共 1153 行
|
||
- `src/services/ai.ts:540-605`、`608-678`、`745-790` 分别手写了 JSON completion、纯文本 completion、流式 completion
|
||
- `src/services/ai.ts:680-697` 手写了多段 JSON 解析兜底
|
||
- `src/services/ai.ts:76-78`、`591-594`、`662-666` 主要依赖 `console.*` 打日志
|
||
|
||
影响:
|
||
|
||
- LLM 行为扩展时容易继续复制请求模板、错误处理、超时逻辑
|
||
- 错误分类不够稳定,观测主要停留在 console 层
|
||
- prompt、transport、fallback、parse 被放在一起,后续测试和替换模型都不够轻
|
||
|
||
建议:
|
||
|
||
- 抽 `llmClient`,统一 transport、timeout、stream、error taxonomy
|
||
- 抽 `llmParsers`,将 JSON parse / plain text parse / suggestion parse 独立
|
||
- 为关键 prompt 输出建立 fixture 测试,至少覆盖 fallback 与异常响应
|
||
- 如果后续要接多个模型,尽早把 provider 层和 prompt 层解耦
|
||
|
||
## P2:手写路由与死代码开始累积
|
||
|
||
证据:
|
||
|
||
- `src/main.tsx:21-34` 采用手写 `pathname.startsWith(...)`
|
||
- `src/components/GameShell.tsx:1511` 存在 `false && showTeamModal`
|
||
|
||
影响:
|
||
|
||
- 路由能力不具备可扩展性,也不利于后续加 404、重定向、权限判断、嵌套路由
|
||
- 死代码继续堆积后,会误导维护者对真实入口和真实 UI 状态的判断
|
||
|
||
建议:
|
||
|
||
- 引入正式路由层,哪怕只做轻量路由也比手写分发更清晰
|
||
- 清理已经废弃的 UI 分支和不可达逻辑
|
||
- 对“临时下线的功能”改为 feature flag 或明确注释,不要用 `false &&`
|
||
|
||
## 建议落地顺序
|
||
|
||
### 第一阶段:先补工程底座
|
||
|
||
- 增加 ESLint / Prettier / EditorConfig
|
||
- 增加 `test` 脚本与 Vitest
|
||
- 把 CI 最小闭环搭起来:类型检查、单测、build、内容校验
|
||
|
||
### 第二阶段:先拆边界,再拆大文件
|
||
|
||
- 先把 Vite 中的编辑器写文件接口、LLM 代理抽走
|
||
- 再把 `GameShell`、`useStoryGeneration`、`useCombatFlow` 按职责拆域
|
||
- 拆分时优先保持外部接口稳定,避免一次性全仓大改
|
||
|
||
### 第三阶段:收敛基础设施
|
||
|
||
- 统一 persistence 层
|
||
- 统一 editor shared 层
|
||
- 统一 AI client 层
|
||
- 拆分 `types.ts`
|
||
|
||
### 第四阶段:降低发布成本
|
||
|
||
- 将 editor 与 game 做更明确的入口拆分
|
||
- 优化 chunk 边界
|
||
- 评估是否把编辑器做成独立 app
|
||
|
||
## 一句话结论
|
||
|
||
这个仓库当前最需要优化的不是“再补几个功能”,而是**把已经验证有效的玩法与工具链,从“靠大文件和经验串起来”升级为“靠清晰边界、统一基础设施和自动化门禁支撑起来”**。只要这一步不做,后续每次加内容、加编辑器能力、加 AI 流程,工程成本都会持续上升。
|