实战教程:重构一个 Vite 应用
本教程模拟一个常见 Issue:Vite 应用首页的“项目列表”组件越来越难维护,需要拆分数据逻辑与展示逻辑,同时保证用户行为不变。
你将练习的不是某个框架技巧,而是一套可以迁移到真实仓库的 Codex 协作方式:
text
复现 -> 建立基线 -> 约束范围 -> 分步重构 -> 针对性测试 -> 全量验证 -> 审查 diff场景与验收标准
假设项目当前结构如下:
text
src/
api/projects.ts
components/ProjectList.vue
pages/HomePage.vue
types/project.ts
tests/
ProjectList.spec.tsProjectList.vue 同时负责请求、加载状态、筛选、排序和渲染。Issue 要求:
- 把请求和状态管理抽到
useProjectscomposable; - 保持现有 DOM 文案、排序规则和空状态不变;
- 请求失败时增加可重试按钮;
- 不增加依赖,不改变后端 API;
- 原有测试继续通过,并补充失败重试测试。
IMPORTANT
重构的核心验收不是“代码更漂亮”,而是外部行为保持稳定。先确认行为基线,再移动职责。
第 0 步:保护现场
开始前记录仓库状态和现有验证命令:
bash
git status --short
cat package.json
pnpm test
pnpm build如果基线已经失败,不要让 Codex默认把失败归因于本次修改。把现有失败的命令、错误摘要和是否允许处理写进任务说明。
推荐在独立分支或工作树进行练习:
bash
git switch -c codex/refactor-project-list第 1 步:只调查,不修改
进入仓库后启动 Codex,并给出第一轮提示:
text
先不要修改文件。
请阅读:
- src/components/ProjectList.vue
- src/api/projects.ts
- src/types/project.ts
- tests/ProjectList.spec.ts
回答:
1. 当前数据从请求到渲染的完整路径;
2. 加载、成功、空数据和失败四种状态如何表现;
3. 哪些行为被测试保护,哪些没有;
4. 抽取 composable 时最容易引入的回归;
5. 建议的最小文件改动范围。人工检查点
不要只看它是否“理解了”。核对下面几个事实:
- 排序是在 API、组件还是计算属性中完成;
- 组件是否在挂载时自动请求;
- 测试是否依赖具体 DOM 文案或调用次数;
- 是否有取消请求、竞态或组件卸载逻辑;
- 错误状态是否已经存在,只是没有重试入口。
如果回答遗漏关键行为,用具体文件或测试纠偏,不要让错误假设进入计划。
第 2 步:要求一个可审查的计划
text
基于刚才的调查,给出一个不超过 5 步的实施计划。
每一步写清:
- 修改哪些文件;
- 保持什么行为不变;
- 增加或调整什么测试;
- 完成后运行哪条验证命令。
不要把格式化、依赖升级或其他组件清理混入本次重构。
如果需要改变公开 props、事件或 API,请先停下来说明。一个合理计划通常是:
- 用现有测试固定排序、空状态和加载行为;
- 新建
useProjects.ts,迁移请求与状态但不接入页面; - 接入
ProjectList.vue,保持模板契约; - 增加失败与重试测试;
- 运行组件测试、全量测试和构建,审查 diff。
计划的价值是暴露风险和顺序,不是制造一份不能调整的长文档。
第 3 步:先补行为测试
如果现有测试没有保护排序或空状态,先让 Codex补基线:
text
先只修改 tests/ProjectList.spec.ts。
补充当前行为测试:
- 请求期间显示现有 loading 文案;
- 项目按当前规则排序;
- 空数组显示现有空状态;
- 不要为尚未实现的重试按钮写测试。
运行这个测试文件并报告结果。若测试暴露现有行为与我们的描述不一致,先停止并说明。这一步能区分“重构导致的变化”和“原先理解错误”。测试应验证用户行为或稳定契约,避免锁死组件内部变量名。
第 4 步:抽取 composable
text
现在实现计划第 2 步:新建 src/composables/useProjects.ts。
要求:
- 复用现有 api/projects.ts 和 Project 类型;
- 暴露 projects、isLoading、error 和 load;
- 保持现有排序语义;
- 不修改 ProjectList.vue;
- 不新增依赖;
- 为 composable 增加最小必要测试。
完成后运行新增测试和类型检查,只报告与本步相关的改动。分步提示的好处是 diff 小、失败定位快。此时 composable 还没接入 UI,即使设计不合适也容易调整。
第 5 步:接入组件并实现重试
text
将 ProjectList.vue 接入 useProjects。
保持:现有 props、事件、成功列表、排序、loading 和空状态文案。
新增:请求失败时显示错误状态和“重试”按钮;点击后再次调用 load;
重试期间按钮不可重复触发请求。
补充失败 -> 点击重试 -> 成功渲染的组件测试。
先运行相关测试;通过后再运行 lint 和类型检查。为什么把重试放在最后
“抽取职责”和“新增行为”是两种风险。先完成等价重构,再增加错误重试,审查者可以分别判断结构变化与产品变化,回滚也更容易。
第 6 步:扩大验证范围
让 Codex运行验证,也要自己复核命令:
text
请执行交付前验证:
1. 相关组件和 composable 测试;
2. 全量单元测试;
3. lint 与类型检查;
4. production build。
不要修改代码来隐藏无关失败。逐条列出命令、退出结果和失败摘要。前端任务还应人工验证:
- 慢网下 loading 与按钮禁用状态;
- 错误后重试是否只发送一次请求;
- 空数据与失败状态是否容易混淆;
- 375px 与桌面宽度是否溢出;
- 浏览器控制台是否有警告或未处理 Promise。
第 7 步:用审查视角检查 diff
可以开启一个新的审查上下文,或使用当前版本支持的审查命令:
bash
codex review --help
git diff --check
git diff --stat
git diff审查提示词:
text
审查当前分支相对 main 的改动。
重点检查:
- 加载、错误、重试是否存在竞态或重复请求;
- 组件卸载后是否可能更新状态;
- 排序和空状态是否发生兼容性变化;
- 测试是否会在错误实现下仍然通过;
- 是否有超出 Issue 范围的修改。
按严重性列出具体问题,给出文件与行号。纯风格偏好不要列为缺陷。第 8 步:生成可用的 PR 摘要
text
根据最终 diff 和实际运行结果,生成 PR 描述,包含:
- 问题与目标;
- 设计选择;
- 用户可观察变化;
- 测试命令及结果;
- 风险、未验证项与回滚方式。
不要声称没有实际执行的测试已经通过。推荐的交付结构:
md
## Summary
- Extract project loading into `useProjects`.
- Preserve existing list, sorting, loading, and empty states.
- Add an explicit error state with retry protection.
## Verification
- `pnpm test ProjectList` - passed
- `pnpm test` - passed
- `pnpm lint` - passed
- `pnpm build` - passed
## Risk
- Request cancellation on rapid route changes was not added; existing behavior is unchanged.
- Manual checks completed at 375px and 1440px.失败时如何收缩任务
| 现象 | 下一步 |
|---|---|
| 修改范围不断扩大 | 回到行为基线,只允许修改一个模块及测试 |
| 测试失败很多且原因混杂 | 先运行单个测试文件,再逐层扩大 |
| Codex 猜测框架 API | 指向项目现有用法和锁定版本,不让它凭记忆迁移 |
| 为通过测试删除断言 | 要求解释失败根因,恢复契约断言 |
| diff 混入格式化 | 单独还原无关格式变更,再继续审查 |
| 方案需要新依赖 | 先比较现有能力、维护成本和包体积,再人工批准 |
从这个案例带走什么
- 重构前先固定外部行为,不把“看起来能跑”当基线;
- 调查、结构迁移和新增行为分开,降低每一步的不确定性;
- 提示词写清停止条件,遇到接口、依赖或范围变化先报告;
- 测试结果、构建结果和未验证项共同构成交付证据;
- 最终责任仍在审查者,Codex 的总结不能替代真实 diff。
资料来源
- OpenAI Prompting:Codex 工作流
- OpenAI Developer commands:
codex review - OpenAI Agent approvals & security
- Simon Willison:How I use LLMs to help me write code
- Addy Osmani:My LLM coding workflow going into 2026
最后校对:2026 年 7 月 22 日