origin-app/
├── app/ # Next.js 应用目录
│ ├── page.tsx # 主页面(重构后仅负责组件编排)
│ ├── layout.tsx # 布局组件
│ └── globals.css # 全局样式
│
├── components/ # 组件层
│ └── quiz/ # 测验相关组件
│ ├── index.ts # 组件统一导出
│ ├── IntroScreen.tsx # 角色选择介绍页面
│ ├── OpeningScreen.tsx # 开场白页面
│ ├── QuizScreen.tsx # 问题页面
│ ├── ResultScreen.tsx # 结果页面
│ └── Footer.tsx # 底部品牌信息
│
├── hooks/ # 自定义 Hooks
│ └── useQuiz.ts # 测验状态管理 Hook
│
├── types/ # 类型定义
│ └── quiz.ts # 测验相关类型定义
│
├── data/ # 数据层
│ └── quizData.tsx # 测验数据和结果消息配置
│
└── public/ # 静态资源
┌─────────────────────────────────────┐
│ app/page.tsx (视图层) │
│ 组件编排 & 路由 │
└──────────────┬──────────────────────┘
│
┌──────────┴──────────┐
│ │
┌───▼─────────┐ ┌──────▼──────┐
│ components │ │ hooks │
│ (UI层) │◄───│ (逻辑层) │
└─────────────┘ └──────┬───────┘
│
┌──────▼──────┐
│ types │
│ (类型层) │
└──────┬──────┘
│
┌──────▼──────┐
│ data │
│ (数据层) │
└─────────────┘
- 职责:组件编排和状态提升
- 不负责:业务逻辑、UI 渲染细节
- 依赖:components、hooks
每个组件专注于单一 UI 职责:
IntroScreen.tsx:角色选择界面OpeningScreen.tsx:开场白展示QuizScreen.tsx:问题和选项展示ResultScreen.tsx:结果展示Footer.tsx:底部信息
- 管理所有测验状态(step、role、currentQ、resultMsg)
- 提供状态操作方法(startQuiz、handleOption、showFinal、reset)
- 将业务逻辑从组件中分离
定义所有 TypeScript 类型:
QuizOption:问题选项Question:问题RoleData:角色数据QuizDataType:测验数据RoleKey:角色键QuizStep:测验步骤
quizData:所有角色的问题和选项resultMessages:结果消息映射(扁平化结构)
重构前(app/page.tsx):
- 370 行代码
- 包含类型、数据、逻辑、UI 全部内容
- 难以维护和扩展
重构后(app/page.tsx):
- 50 行代码
- 仅负责组件编排
- 清晰简洁
每个文件/模块只负责一件事:
- 类型定义 →
types/ - 数据配置 →
data/ - 业务逻辑 →
hooks/ - UI 渲染 →
components/ - 页面编排 →
app/
- 完整的 TypeScript 类型定义
- 避免使用
any类型 - 使用
Record<string, string>处理动态键值对
- 单一职责原则:每个文件职责单一
- 开闭原则:易于扩展,无需修改现有代码
- 依赖倒置:依赖抽象(types)而非具体实现
- 业务逻辑与 UI 分离
- Hooks 可独立测试
- 组件可独立测试
用户交互
↓
UI 组件(components)
↓
调用 Hook 方法(useQuiz)
↓
更新状态(useState)
↓
读取数据(quizData)
↓
返回新状态
↓
UI 重新渲染
- 核心矛盾:控制欲 vs 真爱、投资心态 vs 无条件接纳
- 深层追问:
- 投射:你看到的是孩子,还是你的遗憾?
- 控制:你的爱是自由的,还是有条件的?
- 回报:你的付出是给予,还是投资?
- 后悔:如果重来,你还愿意吗?
- 核心矛盾:工具化 vs 人性、惯性 vs 觉醒
- 深层追问:
- 目的:你清楚自己在为什么而学吗?
- 价值:除了学习,你还是谁?
- 延迟满足:你是在奔向未来,还是在逃避现在?
- 自我:当标签消失,你还剩下什么?
- 核心矛盾:被动生存 vs 主动生活、麻木 vs 觉醒
- 深层追问:
- 麻木:你能分清"工作"和"活着"吗?
- 选择:你的生活是选择的,还是"被选择"的?
- 身份:工作之外,你是谁?
- 存在:你在"活着",还是在"被活着"?
- 在
types/quiz.ts中更新QuizDataType - 在
data/quizData.tsx中添加新角色数据 - 添加对应的结果消息
- UI 自动支持,无需修改组件
- 在
data/quizData.tsx对应角色的questions数组中添加 - 添加对应的结果消息键值对
- 无需修改任何其他代码
- 每个组件的样式都是独立的
- 使用 Tailwind CSS,易于定制
| 模块 | 行数 | 职责 |
|---|---|---|
| app/page.tsx | ~50 | 组件编排 |
| types/quiz.ts | ~45 | 类型定义 |
| data/quizData.tsx | ~260 | 数据配置 |
| hooks/useQuiz.ts | ~75 | 业务逻辑 |
| components/quiz/* | ~200 | UI 组件 |
| 总计 | ~630 | 完整功能 |
- ✅ 无 TypeScript 错误
- ✅ 无 ESLint 警告
- ✅ 完整的类型安全
- ✅ 清晰的代码结构
- ✅ 模块化设计
- ✅ 单一职责
- ✅ 易于测试
- ✅ 易于扩展
- ✅ 从表层问题深入到人性本质
- ✅ 触及内心深处的真实想法
- ✅ 引发深度思考和自我觉察
这次重构展示了:
- 如何将臃肿的单文件拆分成清晰的模块
- 如何设计合理的架构层次
- 如何使用 TypeScript 保证类型安全
- 如何分离关注点(UI、逻辑、数据)
- 如何从人性本质角度设计产品内容
重构完成时间:2025 重构目标:✅ 已达成