Skip to content

架构概览

文档日期:2026-04

技术栈

技术
框架Next.js 16 App Router
UI 库React 19 + Tailwind CSS 4 + framer-motion
语言TypeScript 5(strict 模式)
实时通信原生 WebSocket API
状态管理React Context + useReducer
认证HTTP-only Cookie(JWT)
测试Playwright E2E

核心设计原则

1. 单页游戏路由

/game 页面根据服务端推送的 phase 字段动态渲染对应阶段的 UI,无需多路由切换。这样避免了路由跳转时的状态丢失问题,也使得阶段切换动画更加流畅。

/game  →  GamePhaseRenderer  →  WaitingRoom | SpeakingPhase | TeamSelectPhase | ...

2. 服务端单向数据流

WebSocket 消息

useWebSocket (解析 + dispatch)

gameReducer (纯函数状态转换)

GameContext (状态分发)

各 Phase 组件 (只读 state,调用 sendMessage)

前端不持有任何业务逻辑判断(如"谁是好人"、"任务能否通过"),完全由服务端决策并推送结果。

3. 输入锁由服务端托管

所有交互按钮的 disabled 状态由 GameState.inputLocked 驱动。服务端通过 INPUT_LOCKED / INPUT_UNLOCKED 消息精确控制何时允许用户操作,杜绝重复提交。

目录结构

app/
├── game/
│   ├── page.tsx                    # 游戏主页入口(GameProvider + GameInner)
│   ├── context/
│   │   ├── GameContext.tsx         # React Context + Provider + useGame hook
│   │   └── gameReducer.ts          # 状态 reducer(处理所有 WS 消息)
│   ├── hooks/
│   │   └── useWebSocket.ts         # WebSocket 连接、重连、sendMessage 注入
│   ├── types/
│   │   └── index.ts                # 所有 TypeScript 类型定义
│   └── components/
│       ├── GamePhaseRenderer.tsx   # 根据 phase 渲染对应阶段组件
│       ├── PhaseAnnouncement.tsx   # 阶段切换全屏动画公告
│       ├── RoleRevealModal.tsx     # 游戏开始时角色公示弹窗
│       ├── phases/                 # 各阶段 UI 组件
│       │   ├── WaitingRoom.tsx
│       │   ├── SpeakingPhase.tsx
│       │   ├── TeamSelectPhase.tsx
│       │   ├── TeamVotePhase.tsx
│       │   ├── MissionPhase.tsx
│       │   ├── AssassinPhase.tsx
│       │   └── GameOver.tsx
│       └── ui/                     # 常驻 UI 组件
│           ├── PlayerCard.tsx
│           ├── PlayerList.tsx
│           ├── MissionTracker.tsx
│           ├── RoleCard.tsx
│           ├── PhaseHeader.tsx
│           ├── ToastNotification.tsx
│           ├── VoteResult.tsx
│           ├── MissionResult.tsx
│           └── SpeechHistorySidebar.tsx
├── home/                           # 大厅页面(房间列表)
├── login/                          # 登录页
├── register/                       # 注册页
├── api/                            # Next.js API Routes(反向代理)
│   ├── [...path]/route.ts          # 通用后端 API 代理
│   └── logout/route.ts             # 登出接口
└── components/                     # 全局共享组件
    ├── WolfLogo.tsx
    └── PageBackground.tsx

数据流详解

GameState 初始化

ts
// gameReducer.ts
export const initialGameState: GameState = {
  connected: false,
  phase: "waiting",
  // ...所有字段均有明确初始值
};

消息处理流水线

  1. useWebSocket 接收 MessageEvent,解析为 ServerMessage
  2. dispatch({ type: "SERVER_MESSAGE", message: parsed })
  3. gameReducerSERVER_MESSAGE case 再 switch msg.type
  4. 返回不可变的新 state

E2E 测试钩子

非生产环境下,以下对象被挂载到 window

  • window.__gameDispatch__ — 直接 dispatch action(用于模拟各种游戏状态)
  • window.__gameState__ — 读取当前完整 state

快速开始

bash
# 安装依赖
pnpm install

# 启动开发服务器(默认 localhost:3000)
pnpm dev

# 构建生产版本
pnpm build

文档预览

pnpm docs:dev # 本地预览(含 TypeDoc 自动生成) pnpm docs:build # 生产构建 pnpm docs:preview # 预览生产构建结果

TuringHunt Frontend