13 KiB
13 KiB
工程优化审查报告(2026-03-29)
说明
- 扫描范围:
src/、scripts/、docs/、package.json、vite.config.ts、tsconfig.json - 已执行校验:
npm run lint、npm run build、npm run check:content - 本报告只从工程角度讨论结构、边界、质量门禁、可维护性与可扩展性
- 按仓库说明,暂不讨论中文乱码本身
当前结论
项目当前可构建、可运行、内容校验可通过,说明基础功能链路是通的;但从工程视角看,已经出现明显的“单点过重、边界混杂、质量门禁偏弱、编辑器与运行时耦合”问题。继续叠需求会越来越依赖人工记忆和局部经验,回归风险会持续上升。
当前最值得优先处理的不是单个 UI 细节,而是以下四个工程主题:
- 运行时主链路的职责拆分还不够,核心 hook / 组件已经过载
- 缺少真正的工程质量门禁,
lint目前本质上只是tsc - 编辑器、运行时、类后端能力都混在同一个 Vite 配置里
- 持久化、AI 调用、编辑器保存等基础设施仍然是“分散手写”
运行状态快照
npm run lint通过npm run build通过npm run check:content通过- 应用代码下未发现测试文件:
src/、scripts/、docs/内没有*.test.*/*.spec.* - 构建产物已出现较大 chunk
dist/assets/App-*.js约407 KBdist/assets/itemCatalog-*.js约414 KBdist/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 个useMemosrc/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、背包/锻造 UIsrc/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 --noEmitpackage.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 文件读写 APIvite.config.ts:253直接写回src/data/*.jsonvite.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会在状态变化时自动把完整快照写入localStoragesrc/hooks/useGamePersistence.ts:157-163快照包含gameState + bottomTab + currentStorysrc/hooks/useGamePersistence.ts:68-116恢复逻辑已经开始承担大量 schema 纠偏职责src/data/customWorldLibrary.ts:1-282自定义世界库单独维护一套localStorage读写与 normalizesrc/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、saveJsonObjectsrc/components/StateFunctionEditor.tsx:113-130再次实现cloneValue、SectionCardsrc/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、流式 completionsrc/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 流程,工程成本都会持续上升。