框架切换:Next.js 16 → Vite 5 + React 19 + React Router 7 状态管理:7 手写 hook → 4 Zustand slice 数据获取:直接 fetch → TanStack Query 5 (15 hooks) 表单:3-step 状态机 → RHF 7 + Zod 测试:0 → 25 单测 + 20 e2e (4 浏览器) Bundle:192 KB gzip (目标 < 300 KB) Lint:49 warning → 0 warning Type:33 any → 0 any S2-T-01 切 Vite 5 + React 19 框架 S2-T-02 Zustand 4 slice 替代 7 手写 hook S2-T-03 TanStack Query 5 + 15 hooks S2-T-04 Vitest + RTL + MSW + Chromatic 配置 S2-T-05 OpenAPI Codegen + Zod schema 复用 S2-T-06 RHF 7 + Zod 重写 FormCard S2-T-07 Playwright 视觉基线 4 浏览器 5 spec S2-T-08 Vite build + bundle < 300KB gzip G1 闸门 (全部通过): - typecheck: 0 error - lint: 0 error 0 warning - test (Vitest): 25/25 passed - test:e2e (Playwright): 20/20 passed - build (Vite): 326 modules 4.14s - codegen:check: OpenAPI types 非占位 Sprint 2 收口报告:SPRINT_2_CLOSEOUT_2026-07-03.md
254 lines
12 KiB
Markdown
254 lines
12 KiB
Markdown
# V10 重构策略:UI/交互层与技术层解耦(2026-07-03)
|
||
|
||
> **核心原则(PM 拍板)**:
|
||
> > "原型主要确定 UI 和交互,我们重构前端时要用原型的 UI 和交互,可以不适用原型的技术。"
|
||
>
|
||
> **含义**:原型是 **UI/交互的行为规约**(行为契约),不是 **代码模板**。技术栈完全开放。
|
||
|
||
---
|
||
|
||
## 0. 与 V1 / V2 报告的关键差异
|
||
|
||
| 维度 | V1 / V2 假设 | V10 修正 |
|
||
|---|---|---|
|
||
| 原型代码 | "保留为主,只重构集成" | **可整体重写**,但 UI/交互 1:1 复现 |
|
||
| React 19 | "沿用" | **可选**:Vue 3 / Svelte 5 / Solid 都可以 |
|
||
| useChat 单体 hook | "分解成 5 个子 hook" | **可选**:保留/Zustand/Jotai/Redux Toolkit 都可以 |
|
||
| `any` 类型 33 处 | "降级到 warn,逐步修" | **直接清零**:重写时使用严格类型 |
|
||
| 7 个原型 lib 文件 | "提取到 packages/lib" | **可丢弃**:业务逻辑重新按 API 端点重写 |
|
||
| ESLint 49 warning | "Sprint 1 降级 + Sprint 2 修" | **直接消除**:新代码按严格规范写 |
|
||
| 评估总工时 | 66-89 人天 | **修正 50-70 人天**(节省的 16-19d 来自:删 `any` 修复 + 7 文件重写 + 6 个手写 hook 重构) |
|
||
|
||
---
|
||
|
||
## 1. 双层模型:UI/交互层 × 技术层
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────┐
|
||
│ Layer A · UI/交互契约层(🔒 锁定,1:1 复现) │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ • 12 个 UI 不变量(见 FRONTEND_REFACTOR_PLAN §3.1) │
|
||
│ • 8 个核心组件视觉规范(PlanCard 3-Tab、ModeIndicator 4 │
|
||
│ mode 决策树、FormCard 3-step guards、Badge 状态映射) │
|
||
│ • 1024px 断点、48px 移动端导航、SafeMarkdown XSS 防护 │
|
||
│ • Design Token(247 行 design-system.css 100+ 变量) │
|
||
│ • 暗/亮/系统三主题切换 + 1.2s 缓动 │
|
||
│ • Flash 防护:theme-script + ThemeProvider SSR/CSR 一致 │
|
||
│ • Typing 动画(思考指示器三态:思考中/已暂停/已停止) │
|
||
│ • 移动端手势(下拉刷新、虚拟键盘适配、安全区) │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ ★ 验收:Playwright 视觉回归 + Chromatic 像素级 diff │
|
||
│ ★ 验收:axe-core 0 critical / 0 serious │
|
||
└────────────────────────────────────────────────────────┘
|
||
△ 契约接口
|
||
│
|
||
┌─────────────────────────▽──────────────────────────────┐
|
||
│ Layer B · 技术实现层(🔓 完全开放,按 Sprint 选型) │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ B1. 框架:Next.js 16 / Vite+React 19 / Nuxt 3 / │
|
||
│ SvelteKit 2 / SolidStart 1(任选) │
|
||
│ B2. 状态:Zustand 4 / Jotai 2 / Redux Toolkit 2 / │
|
||
│ Pinia 2 / 自定义 store(任选) │
|
||
│ B3. 数据:TanStack Query 5 / SWR 2 / Apollo 4 / │
|
||
│ tRPC 11(任选) │
|
||
│ B4. 表单:React Hook Form 7 / Formik 2 / VeeValidate │
|
||
│ B5. 测试:Vitest 2 / Jest 29 / Playwright 1.55(统一) │
|
||
│ B6. 构建:Turborepo 2.10 / Nx 20 / Lerna 8(任选) │
|
||
│ B7. 样式:Tailwind 4 / vanilla-extract / CSS Modules / │
|
||
│ Panda CSS / UnoCSS(任选,保持 design token 一致)│
|
||
│ B8. 类型:TypeScript 5(统一)/ JSDoc(备选) │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ ★ 验收:typecheck 0 error / lint 0 error / test 100% │
|
||
│ ★ 验收:bundle < 300KB gzip / LCP < 2.5s / CLS < 0.1 │
|
||
└────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 2. UI/交互契约 · 12 项不变量(必须 1:1 复现)
|
||
|
||
> 来源:`FRONTEND_REFACTOR_PLAN_2026-07-02.md` §3.1,已通过原型实测验证。
|
||
|
||
### 2.1 布局不变量(4 项)
|
||
|
||
| # | 不变量 | 实测值 | 验收方式 |
|
||
|---|---|---|---|
|
||
| L1 | 桌面断点 | `≥ 1024px` 显示侧栏三栏布局 | Playwright viewport=1280 |
|
||
| L2 | 移动断点 | `< 768px` 单栏 + 底部 Tab 导航(48px 高) | Playwright viewport=375 |
|
||
| L3 | 中间断点 | `768-1023px` 双栏,导航折叠为顶部抽屉 | Playwright viewport=900 |
|
||
| L4 | 容器宽度 | `max-w-7xl mx-auto px-4 sm:px-6 lg:px-8` | 视觉回归 |
|
||
|
||
### 2.2 组件不变量(4 项)
|
||
|
||
| # | 组件 | 关键行为 | 来源文件 |
|
||
|---|---|---|---|
|
||
| C1 | **PlanCard 3-Tab** | "院校 / 专业 / 就业" Tab 切换不重渲染父组件 | `src/components/PlanCard.tsx` |
|
||
| C2 | **ModeIndicator 4-mode 决策树** | chat-mode / plan-mode / review-mode / share-mode 互斥显示,决策延迟 < 50ms | `src/components/ModeIndicator.tsx` |
|
||
| C3 | **FormCard 3-step guards** | step 1→2 需 score 输入;step 2→3 需选科+位次;后退保留数据 | `src/components/FormCard.tsx` |
|
||
| C4 | **Badge 8 态映射** | 985/211/双一流/省重点/普通本科/专科/提前批/征集,颜色 + 图标对应 | `src/components/Badge.tsx` |
|
||
|
||
### 2.3 行为不变量(2 项)
|
||
|
||
| # | 行为 | 验收 |
|
||
|---|---|---|
|
||
| B1 | **Typing 动画三态** | "AI 正在思考"旋转 / "已暂停"静止 / "已停止"隐藏 | Playwright trace |
|
||
| B2 | **SafeMarkdown XSS 防护** | `<script>` / `onerror` / `javascript:` 全部过滤 | unit test + xss-payload 库 |
|
||
|
||
### 2.4 设计系统不变量(2 项)
|
||
|
||
| # | 不变量 | 来源 |
|
||
|---|---|---|
|
||
| D1 | **247 行 design-system.css 100+ token** | `src/styles/design-system.css` |
|
||
| D2 | **三主题切换 + 1.2s 缓动 + SSR/CSR 一致** | `src/styles/globals.css` + `src/components/ThemeProvider.tsx` |
|
||
|
||
---
|
||
|
||
## 3. 技术选型决策菜单(Sprint 2 启动前必选)
|
||
|
||
PM 需要在 Sprint 2 启动前从以下 8 个轴中做选择(或选"沿用原型"):
|
||
|
||
### 选项 A:沿用原型技术栈(最小风险)
|
||
```
|
||
Next.js 16 + React 19 + TypeScript 5 + Tailwind 4 + 自定义 useChat
|
||
工时:~50 人天(节省技术调研)
|
||
风险:低(已在 Sprint 1 验证 G0)
|
||
```
|
||
|
||
### 选项 B:现代化重写(推荐 ★)— 与 V2 任务清单一致
|
||
```
|
||
Vite 5 + React 19 + TypeScript 5 + Tailwind 4
|
||
+ Zustand 4(替代 7 个手写 hook)
|
||
+ TanStack Query 5(数据获取)
|
||
+ React Hook Form 7(表单)
|
||
+ Vitest 2 + Playwright 1.55
|
||
工时:~58 人天(多 8d 调研/重写,节省 16d `any` 修复 + 6 hook 重构)
|
||
净节省:8 人天
|
||
风险:中(需 Chromatic 全量视觉回归)
|
||
```
|
||
|
||
### 选项 C:激进换框架(最大创新)
|
||
```
|
||
Nuxt 3 + Vue 3.5 + TypeScript 5 + Pinia 2
|
||
+ VueUse 11(替代 7 个手写 hook)
|
||
+ TanStack Query 5(Vue 版)
|
||
+ VeeValidate 4
|
||
+ Vitest 2 + Playwright 1.55
|
||
工时:~68 人天(多 18d 框架迁移)
|
||
风险:高(团队 Vue 经验需评估)
|
||
```
|
||
|
||
### 选项 D:性能优先(高 LCP 压力场景)
|
||
```
|
||
Astro 5 + React 19 岛模式 + TypeScript 5
|
||
+ Zustand 4 + TanStack Query 5
|
||
工时:~72 人天
|
||
风险:低(岛模式天然 SSR + 局部 hydrate)
|
||
```
|
||
|
||
### 选项 E:保留 Next.js + 改状态管理
|
||
```
|
||
Next.js 16 App Router + React 19 Server Components
|
||
+ Zustand 4(client store)
|
||
+ TanStack Query 5
|
||
工时:~55 人天
|
||
风险:低-中
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Sprint 2-8 计划修正矩阵
|
||
|
||
| Sprint | 主题 | V2 估时 | V10 估时(选项 B) | 关键变化 |
|
||
|---|---|---|---|---|
|
||
| **S1** Foundation | monorepo + 5 基础组件 | 11d | 11d | ✅ 已完成 |
|
||
| **S2** API Hooks | 5 模块 × 3 hook = 15 个 | 12d | **9d** | Zustand store 替代 15 个手写 hook |
|
||
| **S3** New Modules | 5 个新模块(Share/Query/Review/LLM/Poster) | 14d | **12d** | 删 `any` 同步重写 + TanStack Query |
|
||
| **S4** Perf/Ops | 性能优化 + 监控 | 10d | 10d | 不变 |
|
||
| **S5** Components | 高级组件(DataTable/Tree/Chart) | 11d | **9d** | TanStack Virtual + TanStack Table |
|
||
| **S6** Query/Review | 查询/审核 UI | 12d | 12d | 不变 |
|
||
| **S7** A11y/Portal | 无障碍 + 招生门户 | 10d | 9d | axe-core CI 集成 |
|
||
| **S8** Admin/Policy | 后台 + 政策合规 | 12d | 10d | 简化的 RHF 表单 |
|
||
| **合计** | | **92d** | **82d** | 节省 10 人天 |
|
||
|
||
> 注:Sprint 2 节省 3d / Sprint 3 节省 2d / Sprint 5 节省 2d / Sprint 7 节省 1d / Sprint 8 节省 2d = 10d。
|
||
> V2 任务清单的 14 个新模块任务依然有效,但实现路径从"修原型"变为"重写 + 严格类型"。
|
||
|
||
---
|
||
|
||
## 5. 关键问题清单(V10 启动前 PM 必答)
|
||
|
||
### Q1:技术栈选哪个?
|
||
- A:沿用原型
|
||
- **B:Vite+React+Zustand+TanStack Query+RHF(推荐)** ★
|
||
- C:Nuxt+Vue+Pinia
|
||
- D:Astro 岛模式
|
||
- E:Next.js + Zustand
|
||
|
||
### Q2:UI/交互层验收方式?
|
||
- Playwright 视觉回归 + Chromatic 像素 diff(推荐 ★)
|
||
- 仅人工抽查(不推荐)
|
||
- 第三方视觉测试(如 Percy)
|
||
|
||
### Q3:原型代码处置?
|
||
- **整体重写为新实现(推荐 ★)**:UI 1:1 + 严格类型 + 现代状态管理
|
||
- 保留 + 局部重构:保留 useChat 单体 hook,渐进式重构
|
||
- 二者并行:保留原型做 A/B 对照
|
||
|
||
### Q4:Sprint 2 启动条件?
|
||
- Q1 选定 + Q2 选定 + Q3 选定后方可启动
|
||
- 默认值:推荐 B + Chromatic + 整体重写
|
||
|
||
### Q5:是否需要更新所有 Sprint 2-8 文档?
|
||
- **是(推荐 ★)**:根据技术选型重新拆分子任务,避免 S2 启动后再发现颗粒度问题
|
||
- 否:保持 V2 文档,按当前颗粒度执行,遇到问题再调整
|
||
|
||
---
|
||
|
||
## 6. 与 V2 任务清单的关系(V10 = V2 选型版)
|
||
|
||
```
|
||
V1(基础)→ V2(+14 人天,5 新模块)→ V10(+技术选型 + UI/交互契约锁定)
|
||
↓ ↓ ↓
|
||
方案层 任务层 决策层
|
||
```
|
||
|
||
**V10 输出的决策**将作为 Sprint 2 启动的输入。一旦 PM 选定 Q1-Q3,所有 Sprint 2-8 子任务将按选定技术栈重新拆解,颗粒度仍为 0.25 人天。
|
||
|
||
---
|
||
|
||
## 7. 建议执行顺序
|
||
|
||
```
|
||
1. PM 审阅 V10 文档(当前)
|
||
2. PM 决策 Q1-Q5(5 分钟)
|
||
3. 高级开发工程师根据决策更新 SPRINT_2-8 文档(2 人天)
|
||
4. 启动 Sprint 2(按更新后的子任务执行)
|
||
5. 每个 Sprint 完成后做 Chromatic 视觉回归 + axe-core + bundle 体积报告
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 风险登记(V10 特有)
|
||
|
||
| ID | 风险 | 等级 | 缓解 |
|
||
|---|---|---|---|
|
||
| V10-R1 | UI 1:1 复现遗漏 | 中 | Chromatic 全量视觉基线 + Playwright 像素比对 |
|
||
| V10-R2 | 新技术栈团队不熟 | 中 | Sprint 2 前 1d spike(小型 PoC) |
|
||
| V10-R3 | 整体重写工期超 V2 估时 | 低 | 保留 7d 缓冲;S2 中期 gate 评估 |
|
||
| V10-R4 | 与后端 API 契约错位 | 中 | OpenAPI 自动生成类型 + MSW mock |
|
||
| V10-R5 | 设计 token 在新栈丢失 | 中 | 设计 token 在 D1 中提前抽取至 packages/ui/tokens |
|
||
|
||
---
|
||
|
||
## 9. 决策记录
|
||
|
||
| 日期 | 决策 | 决策人 | 影响 |
|
||
|---|---|---|---|
|
||
| 2026-07-03 | 原型只锁 UI/交互,不锁技术 | PM | V10 文档出,V1-V2 重构范围调整 |
|
||
| 待定 | Q1-Q5 5 项决策 | PM | Sprint 2-8 文档更新 + 启动 |
|
||
|
||
---
|
||
|
||
**V10 文档状态**:草稿 v1,待 PM 决策 Q1-Q5
|
||
**下一步**:等待 PM 决策(建议选项 B + Chromatic + 整体重写 + 是更新文档)
|