Files
file-transfer-go/FRONTEND_ANALYSIS.md

472 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端代码架构分析 & 优化计划
> 最后更新2026-02-28
---
## 目录
- [一、全局数据](#一全局数据)
- [二、文件清单与行数](#二文件清单与行数)
- [三、核心问题分析](#三核心问题分析)
- [3.1 过度抽象的连接层5 层 Hook 嵌套)](#31-过度抽象的连接层5-层-hook-嵌套)
- [3.2 "Shared" 名不副实 — 多处独立创建连接](#32-shared-名不副实--多处独立创建连接)
- [3.3 WebRTCFileTransfer 组件是复杂度炸弹](#33-webrtcfiletransfer-组件是复杂度炸弹)
- [3.4 到处重复的类型定义和验证逻辑](#34-到处重复的类型定义和验证逻辑)
- [3.5 useTabNavigation 不应耦合连接管理](#35-usetabnavigation-不应耦合连接管理)
- [3.6 其他问题汇总](#36-其他问题汇总)
- [四、优化计划](#四优化计划)
- [Phase 1消除重复、统一类型](#phase-1消除重复统一类型)
- [Phase 2扁平化 Hook 层](#phase-2扁平化-hook-层)
- [Phase 3拆分超级组件](#phase-3拆分超级组件)
- [Phase 4基础设施清理](#phase-4基础设施清理)
- [五、优化总览](#五优化总览)
---
## 一、全局数据
| 指标 | 数值 |
|------|------|
| 总行数 | **11,776 行** (69 个 .ts/.tsx 文件) |
| Hooks 文件 | **19 个**, ~3,500 行 |
| 组件文件 | **20 个**, ~5,500 行 |
| `console.log/warn/error` | **428 处** |
| `useEffect` 调用 | **75 处** |
| `setTimeout` / 延时等待 | **28 处** |
| `FileInfo` 接口重复定义 | **7 处** |
---
## 二、文件清单与行数
### 2.1 Hooks 层19 个文件,~3,500 行)
#### Connection 模块8 个文件,~1,640 行)
| 文件 | 行数 | 关键导出 | 职责 |
|------|------|----------|------|
| `hooks/connection/index.ts` | 5 | 4 个 re-export | 桶文件 |
| `hooks/connection/useWebRTCConnectionCore.ts` | **569** | `useWebRTCConnectionCore()` | **最大最复杂的 hook** — WebSocket 连接、PeerConnection 创建、信令消息处理(offer/answer/ICE/peer-joined/disconnection)、房间管理 |
| `hooks/connection/useWebRTCDataChannelManager.ts` | **355** | `useWebRTCDataChannelManager()` | 数据通道创建(sender/receiver 分支)、消息/二进制数据分发、通道注册 |
| `hooks/connection/useWebRTCTrackManager.ts` | 229 | `useWebRTCTrackManager()` | 媒体轨道添加/移除、createOffer、onTrack 轮询重试 |
| `hooks/connection/useWebRTCStateManager.ts` | 77 | `useWebRTCStateManager()` | 对 zustand store 的薄封装 |
| `hooks/connection/useSharedWebRTCManager.ts` | 118 | `useSharedWebRTCManager()``WebRTCConnection` | **门面模式** — 组合 4 个子 manager暴露统一的 `WebRTCConnection` 接口 |
| `hooks/connection/useConnectionState.ts` | 137 | `useConnectionState()` | 连接错误展示/状态清理的 side-effect hook |
| `hooks/connection/useRoomConnection.ts` | 110 | `useRoomConnection()` | 房间验证逻辑(HTTP check + connect) |
| `hooks/connection/useWebRTCSupport.ts` | 40 | `useWebRTCSupport()` | WebRTC 浏览器兼容性检测 |
**复杂度观察:**
- `useWebRTCConnectionCore.ts` 是**整个代码库最复杂的文件569 行)**。它在 `ws.onmessage` 中处理了 7 种信令消息类型,每种都有嵌套的 `if/else` 分支处理 sender/receiver 角色、reconnect 状态、PeerConnection 是否存在。特别是 `answer` 处理(约 L268-L350有 3 层 `if/else` 嵌套 + 异常恢复逻辑。
- `useWebRTCDataChannelManager` 中 sender 和 receiver 的 `onerror` 处理器是**完全重复的代码**(各约 30 行相同的 `switch` 语句)。
- `useWebRTCTrackManager.onTrack` 中有**轮询重试机制**50 次 × 每 100ms是一种脆弱的模式。
#### File-Transfer 模块4 个文件,~916 行)
| 文件 | 行数 | 关键导出 | 职责 |
|------|------|----------|------|
| `hooks/file-transfer/index.ts` | 4 | re-export | 桶文件 |
| `hooks/file-transfer/useFileTransferBusiness.ts` | **676** | `useFileTransferBusiness(connection)` | **第二大 hook** — 文件分块传输(256KB chunks)、CRC32 校验和、ACK 确认、重试(5 次指数退避)、流控、进度追踪 |
| `hooks/file-transfer/useFileStateManager.ts` | 171 | `useFileStateManager()` | 文件选择、文件列表维护、进度状态管理 |
| `hooks/file-transfer/useFileListSync.ts` | 65 | `useFileListSync()` | 防抖式文件列表同步 |
**复杂度观察:**
- `useFileTransferBusiness` 内含**自实现的 CRC32 校验算法**`calculateChecksum`, `simpleChecksum`)和完整的**可靠传输协议**ACK/重传/超时/流控),这本质上是在应用层重建了 TCP 的功能。
- `useFileStateManager` 有 3 个 `useEffect` 都在监听并同步文件列表,任何一个变化都可能触发链式更新,存在复杂的依赖关系。
#### Text-Transfer 模块2 个文件,~177 行)
| 文件 | 行数 | 关键导出 |
|------|------|----------|
| `hooks/text-transfer/index.ts` | 2 | re-export |
| `hooks/text-transfer/useTextTransferBusiness.ts` | 175 | `useTextTransferBusiness(connection)` |
结构简洁。发送 `text-sync``text-typing` 消息。连接状态从 `connection` 参数同步。
#### Desktop-Share 模块2 个文件,~545 行)
| 文件 | 行数 | 关键导出 |
|------|------|----------|
| `hooks/desktop-share/index.ts` | 2 | re-export |
| `hooks/desktop-share/useDesktopShareBusiness.ts` | **543** | `useDesktopShareBusiness()` |
**复杂度观察:**
- 与 file-transfer/text-transfer 不同,**桌面共享直接内部调用** `useSharedWebRTCManager()`,而非从外部接收一个 `connection` 参数。这导致了**不一致的依赖注入模式**。
- `setupVideoSending` 中有大量 `await new Promise(resolve => setTimeout(resolve, ...))` 等待连接稳定的代码500ms + 2000ms是一种脆弱的时序控制。
#### Settings 模块3 个文件,~283 行)
| 文件 | 行数 | 关键导出 |
|------|------|----------|
| `hooks/settings/index.ts` | 3 | re-export |
| `hooks/settings/useIceServersConfig.ts` | 251 | `useIceServersConfig()`, `getIceServersConfig()` |
| `hooks/settings/useWebRTCConfigSync.ts` | 29 | `useWebRTCConfigSync()` |
`useIceServersConfig` 同时导出 hookReact 组件用)和独立函数 `getIceServersConfig()`(非组件代码用),设计合理。
#### UI 模块5 个文件,~496 行)
| 文件 | 行数 | 关键导出 |
|------|------|----------|
| `hooks/ui/webRTCStore.ts` | 46 | `useWebRTCStore` (zustand) |
| `hooks/ui/useTabNavigation.ts` | 183 | `useTabNavigation()` |
| `hooks/ui/useURLHandler.ts` | 210 | `useURLHandler()` |
| `hooks/ui/useConfirmDialog.ts` | 52 | `useConfirmDialog()` |
| `hooks/ui/index.ts` | 5 | re-export |
**复杂度观察:**
- `useTabNavigation` **内部调用了** `useSharedWebRTCManager()``useURLHandler()``useConfirmDialog()`,使得一个 "UI Tab 导航" hook 深度耦合了 WebRTC 连接管理逻辑。
- `useURLHandler` 是泛型 hook支持 `modeConverter` 进行模式映射(如 desktop share 的 `share/view``send/receive`)。
---
### 2.2 组件层20 个文件,~5,500 行)
#### 顶层业务组件
| 文件 | 行数 | 关键导出 | 职责 |
|------|------|----------|------|
| `components/WebRTCFileTransfer.tsx` | **658** | `WebRTCFileTransfer` | **最大的组件** — 文件传输页面容器,组合 7 个 hooks + 2 个子组件 |
| `components/WebRTCTextImageTransfer.tsx` | 104 | `WebRTCTextImageTransfer` | 文本传输页面容器,较简洁 |
| `components/DesktopShare.tsx` | 199 | `DesktopShare` (default) | 桌面共享页面容器,含 `useScreenShareSupport` 内部 hook |
| `components/WebRTCSettings.tsx` | 565 | `WebRTCSettings` (default) | ICE 服务器配置页(自包含,含 `AddServerModal` 内部组件) |
#### WebRTC 子组件(纯展示/交互层)
| 文件 | 行数 | 职责 |
|------|------|------|
| `components/webrtc/WebRTCFileUpload.tsx` | 357 | 发送方文件列表 UI拖拽上传、文件展示、取件码展示 |
| `components/webrtc/WebRTCFileReceive.tsx` | 399 | 接收方取件码输入 + 文件下载列表 UI |
| `components/webrtc/WebRTCTextSender.tsx` | **465** | 文本发送方(**内含自己的连接管理逻辑** |
| `components/webrtc/WebRTCTextReceiver.tsx` | **385** | 文本接收方(**内含自己的连接管理逻辑** |
| `components/webrtc/WebRTCDesktopSender.tsx` | 325 | 桌面共享发送方 |
| `components/webrtc/WebRTCDesktopReceiver.tsx` | 370 | 桌面共享接收方(**含重复的房间验证逻辑** |
**复杂度观察:**
- `WebRTCTextSender``WebRTCTextReceiver` **各自独立调用** `useSharedWebRTCManager()`,然后创建 `useTextTransferBusiness(connection)``useFileTransferBusiness(connection)`。这意味着**每个子组件都创建了自己的独立 WebRTC 连接实例**。
- `WebRTCDesktopReceiver` 中有两处几乎完全相同的房间验证逻辑(`handleJoinViewing``autoJoin` useEffect代码重复约 ~80 行。
- `WebRTCFileReceive` 中也有独立的房间验证代码(`validatePickupCode`),与 `useRoomConnection` 中的 `checkRoomStatus` 功能重复。
#### 共享/展示组件
| 文件 | 行数 | 职责 |
|------|------|------|
| `components/ConnectionStatus.tsx` | 241 | 连接状态 UI3 种模式full/compact/inline |
| `components/WebRTCConnectionStatus.tsx` | 185 | WebRTC 连接状态 UI + `WebRTCStatusIndicator` |
| `components/RoomInfoDisplay.tsx` | 123 | 取件码/QR 码展示通用组件 |
| `components/QRCodeDisplay.tsx` | 68 | QR 码 canvas 渲染 |
| `components/DesktopViewer.tsx` | **546** | 桌面视频播放器(全屏/统计/控制/鼠标隐藏) |
| `components/Hero.tsx` | 42 | 页面顶部标题/链接 |
| `components/RoomStatusDisplay.tsx` | 39 | 房间实时状态展示 |
| `components/WebRTCUnsupportedModal.tsx` | 186 | 浏览器不支持 WebRTC 弹窗 |
| `components/WeChatGroup.tsx` | 68 | 微信群二维码页 |
`ConnectionStatus``WebRTCConnectionStatus` 是**两个独立的连接状态组件**,功能高度重叠但接口不同:前者使用 zustand store后者接受 `WebRTCConnection` prop。
---
### 2.3 页面层
| 文件 | 行数 | 职责 |
|------|------|------|
| `app/page.tsx` | 13 | 入口,渲染 `HomePageWrapper` |
| `app/HomePageWrapper.tsx` | 14 | Suspense 包装层 |
| `app/HomePage.tsx` | 206 | **主页面** — Tab 布局,渲染 5 个 Tab 内容 |
| `app/layout.tsx` | 40 | RootLayout + `ToastProvider` |
| `app/help/HelpPage.tsx` | 761 | 帮助页面 |
### 2.4 Types
`types/index.ts`51 行)— 定义了 `FileInfo`, `TransferProgress`, `RoomStatus`, `FileChunk`, `WebSocketMessage`, `UseWebSocketReturn`
**问题:** `FileInfo` 类型在此文件中定义了一次,但在 `WebRTCFileTransfer.tsx``WebRTCFileUpload.tsx``WebRTCFileReceive.tsx``useFileTransferBusiness.ts``useFileStateManager.ts``useFileListSync.ts` 中各自**重新定义了完全相同的 `FileInfo` 接口**(至少 7 处重复定义)。`UseWebSocketReturn` 类型实际没有被任何代码使用。
### 2.5 Lib 工具层
| 文件 | 行数 | 职责 |
|------|------|------|
| `lib/config.ts` | 150 | 环境配置、URL 构造(`getWsUrl`, `getBackendUrl` 等) |
| `lib/api-utils.ts` | 144 | 统一 fetch 封装(`apiFetch`, `apiGet`, `apiPost`...) |
| `lib/client-api.ts` | 119 | `ClientAPI`OOP style API 封装) |
| `lib/webrtc-support.ts` | 162 | WebRTC 特性检测 + 浏览器信息 |
| `lib/utils.ts` | 6 | `cn()` — tailwind 合并工具 |
| `lib/static-config.ts` | 35 | 静态/动态页面路由定义 |
**问题:** `api-utils.ts``client-api.ts` 是**两套并行的 API 调用方案**。`ClientAPI` 是 class-based 封装,`apiFetch` 是函数式封装,功能完全重叠。实际代码中组件大多直接调用 `fetch()`,两个 utils 文件利用率低。
---
## 三、核心问题分析
### 3.1 过度抽象的连接层5 层 Hook 嵌套)
从 UI 按钮点击到实际网络传输,需穿越 **5 层抽象**
```
组件 (WebRTCFileTransfer) 658 行
└→ useFileTransferBusiness(connection) 676 行
└→ useSharedWebRTCManager() 118 行 ← 门面
├→ useWebRTCStateManager() 77 行 ← zustand 薄封装
├→ useWebRTCDataChannelManager() 355 行 ← DC 管理
├→ useWebRTCTrackManager() 229 行 ← 轨道管理
└→ useWebRTCConnectionCore() 569 行 ← WS+信令+PC
```
`useWebRTCStateManager` 只是对 47 行的 zustand store 做了 `useCallback` 包装,**额外增加 77 行代码但零业务价值**。`useSharedWebRTCManager` 又对 4 个子 manager 做了一层门面包装。
**状态流动图:**
```
zustand Store (webRTCStore)
↑ 写入
useWebRTCStateManager
↑ 使用
useWebRTCConnectionCore / useWebRTCDataChannelManager
↑ 组合
useSharedWebRTCManager → 返回 WebRTCConnection 对象
↑ 调用 ↓ 传入
组件层 useFileTransferBusiness(connection)
(WebRTCFileTransfer) useTextTransferBusiness(connection)
↓ 使用 useDesktopShareBusiness() ← 内部直接调用 Manager
useFileStateManager
useFileListSync
useConnectionState
useRoomConnection
useURLHandler
useTabNavigation
```
### 3.2 "Shared" 名不副实 — 多处独立创建连接
`useSharedWebRTCManager()`**4 处被独立调用**,每次返回新实例:
| 调用位置 | 文件 | 行号 | 问题 |
|----------|------|------|------|
| 文件传输 | `WebRTCFileTransfer.tsx` | L37 | 文件传输的连接 |
| 文字发送 | `WebRTCTextSender.tsx` | L34 | **自己创建连接** |
| 文字接收 | `WebRTCTextReceiver.tsx` | L40 | **自己创建连接** |
| 桌面共享 | `useDesktopShareBusiness.ts` | L15 | 在 hook 内部直接调用 |
每个组件各自调用 `useSharedWebRTCManager()` → 各自创建独立的 PeerConnection 和 DataChannel。**"shared" 名不副实** —— hook 每次调用返回新实例。
`useDesktopShareBusiness` 是**第四种模式**:在 hook 内部自行调用 `useSharedWebRTCManager()`,与 file/text 模块的依赖注入模式不一致。
### 3.3 WebRTCFileTransfer 组件是复杂度炸弹
658 行的组件内:
- **使用 7 个 Hook**`useSharedWebRTCManager`, `useFileTransferBusiness`, `useFileListSync`, `useFileStateManager`, `useRoomConnection`, `useURLHandler`, `useConnectionState`
- **9 个 `useEffect`**:多个 effect 监听重叠的状态(`isConnected`, `isConnecting`, `error`),一次状态变化触发多个 effect 连锁执行
- **与 `useConnectionState` 完全重复的错误处理**
`WebRTCFileTransfer.tsx` 第 316-368 行的错误处理 if/else 链:
```typescript
// 组件内的错误处理 (L316-L368)
if (error.includes('WebSocket')) {
errorMessage = '服务器连接失败...';
} else if (error.includes('数据通道')) { ... }
```
`useConnectionState.ts` 第 46-64 行**完全相同**
```typescript
// Hook 内的错误处理 (L46-L64) — 一模一样的 if/else
if (error.includes('WebSocket')) {
errorMessage = '服务器连接失败...';
} else if (error.includes('数据通道')) { ... }
```
**结果:同一个错误被 Toast 弹出两次。**
**9 个 useEffect 明细:**
| # | 监听依赖 | 职责 | 问题 |
|---|---------|------|------|
| 1 | `onFileListReceived, mode` | 文件列表接收 | 正常 |
| 2 | `onFileReceived, updateFileStatus` | 文件接收完成 | 正常 |
| 3 | `onFileProgress, mode, isConnected, error` | 进度更新 | 正常 |
| 4 | `onFileRequested, mode, selectedFiles, ...` | 文件请求处理 | 正常 |
| 5 | `error, mode, showToast, lastError` | 错误处理 | ⚠️ 与 useConnectionState 重复 |
| 6 | `isWebSocketConnected, isConnected, isConnecting, ...` | 连接状态清理 | ⚠️ 与 useConnectionState 重复 |
| 7 | `isConnected, isPeerConnected, isConnecting, ...` | 日志输出 | ⚠️ 纯日志,可删除 |
| 8 | `connection.isPeerConnected, mode, syncFileListToReceiver` | P2P 建立时同步 | 正常 |
| 9 | `selectedFiles, mode, pickupCode` | 文件选择变化同步 | ⚠️ 与 useFileStateManager 部分重复 |
### 3.4 到处重复的类型定义和验证逻辑
**`FileInfo` 接口在 7 个文件中各自定义**,且字段不完全一致:
| 文件 | 行号 | 字段差异 |
|------|------|---------|
| `types/index.ts` | L2 | `export interface FileInfo`**无 status/progress**,有 lastModified |
| `WebRTCFileTransfer.tsx` | L13 | `interface FileInfo`**有 status/progress** |
| `WebRTCFileUpload.tsx` | L10 | `interface FileInfo` — 有 status/progress |
| `WebRTCFileReceive.tsx` | L10 | `interface FileInfo` — 有 status/progress |
| `useFileListSync.ts` | L3 | `interface FileInfo` — 有 status/progress |
| `useFileStateManager.ts` | L3 | `interface FileInfo` — 有 status/progress |
| `useFileTransferBusiness.ts` | L25 | `interface FileInfo` — 有 status/progress |
**房间验证逻辑至少有 4 处重复实现**
| 位置 | 函数名 | 行数 |
|------|--------|------|
| `hooks/connection/useRoomConnection.ts` | `checkRoomStatus` + `joinRoom` | ~60 行 |
| `components/webrtc/WebRTCFileReceive.tsx` | `validatePickupCode` | ~40 行 |
| `components/webrtc/WebRTCTextReceiver.tsx` | `joinRoom` | ~50 行 |
| `components/webrtc/WebRTCDesktopReceiver.tsx` | `handleJoinViewing` + `autoJoin` | ~80 行 |
### 3.5 useTabNavigation 不应耦合连接管理
`useTabNavigation.ts` 是一个 **UI 导航 hook**,却 import 并调用了:
- `useSharedWebRTCManager()` — 获取 `disconnect` 方法
- `useWebRTCStore` — 直接读取连接状态
- `useURLHandler()` — URL 管理
- `useConfirmDialog()` — 确认弹窗
Tab 切换逻辑不应该知道 WebRTC 的存在,连接生命周期应由更上层管理。
### 3.6 其他问题汇总
| 问题 | 严重程度 | 详情 |
|------|---------|------|
| **428 条 console.log** 散布在生产代码 | 🟡 中 | 使用 emoji 前缀(如 🔧🚀📤),无级别控制,生产环境产生大量噪音 |
| **两个功能重叠的连接状态组件** | 🟡 中 | `ConnectionStatus`241 行,使用 zustand+ `WebRTCConnectionStatus`185 行,使用 prop426 行总计 |
| **两套 API 封装并存** | 🟡 中 | `api-utils.ts`(函数式)+ `client-api.ts`class-based263 行,但组件大多直接用 `fetch()` |
| **`types/index.ts` 中的死代码** | 🟢 低 | `UseWebSocketReturn` 等类型无人引用 |
| **28 处 `setTimeout` 作为同步机制** | 🟡 中 | 500ms/2000ms 硬编码等待连接稳定,在低性能设备上可能不够,在高性能设备上浪费时间 |
| **`useWebRTCTrackManager.onTrack` 轮询** | 🟢 低 | 50 次 × 100ms 轮询等待轨道就绪,应改为事件驱动 |
| **DataChannel `onerror` 处理重复** | 🟡 中 | sender 和 receiver 分支中约 30 行完全相同的 switch 语句 |
---
## 四、优化计划
### Phase 1消除重复、统一类型
> 影响大,改动小。预估耗时:半天。
| 任务 | 预估时间 | 效果 |
|------|---------|------|
| 统一 `FileInfo`(含 status/progress`types/index.ts`,删除 7 处重复定义 | 30min | **-60 行**,消除类型漂移 |
| 抽取 `validateRoom(code)` 通用函数,替代 4 处房间验证 | 30min | **-200 行** |
| 合并 `ConnectionStatus` + `WebRTCConnectionStatus` 为一个组件 | 1h | **-150 行** |
| 删除 `useConnectionState` hook其逻辑已在组件中重复 | 15min | **-138 行**,修复双重 Toast |
| 删除 `api-utils.ts``client-api.ts`,统一为一套 | 30min | **-140 行** |
**Phase 1 预估净减少:~690 行**
---
### Phase 2扁平化 Hook 层
> 核心改造风险中等。预估耗时1-2 天。
**目标5 层 → 3 层**
```
当前 5 层:
组件 → 业务Hook → SharedManager → SubManagers(4个) → Native API
目标 3 层:
组件 → 业务Hook → useWebRTCConnection(合并) → Native API
```
**具体步骤:**
1. **删除 `useWebRTCStateManager`77 行)**
- 直接在 `useWebRTCConnectionCore` 中使用 `useWebRTCStore`
- 一个 hook 包装另一个 hook 再包装 zustand store 毫无必要
2. **合并 `useSharedWebRTCManager` + `useWebRTCConnectionCore`**
- `useSharedWebRTCManager` 只是 4 个子模块的胶水代码
- 将其与 `useWebRTCConnectionCore` 合并为 `useWebRTCConnection`
- 内联数据通道和轨道管理逻辑
- 用清晰的函数分组而非文件拆分来组织代码
3. **统一连接注入模式**
-`useDesktopShareBusiness` 也改为接受 `connection` 参数
- 与 file/text 保持一致
**改造后的目标结构:**
```
hooks/
useWebRTCConnection.ts ← 合并后的唯一连接 hook (~600行)
useFileTransfer.ts ← file-transfer 业务
useTextTransfer.ts ← text-transfer 业务
useDesktopShare.ts ← desktop 业务(接受 connection 参数)
useIceServersConfig.ts ← 保留
webRTCStore.ts ← 保留(直接使用,不再包装)
useURLHandler.ts ← 保留
useTabNavigation.ts ← 删除 WebRTC 依赖
useConfirmDialog.ts ← 保留
```
**预估净减少:~400 行 + 消除 2 层抽象**
---
### Phase 3拆分超级组件
> 降低单文件复杂度。预估耗时1 天。
**`WebRTCFileTransfer.tsx`658 行)→ 拆分为:**
| 新文件 | 职责 | 预估行数 |
|--------|------|----------|
| `WebRTCFileTransfer.tsx` | 仅做 mode 切换 + 子组件渲染 | ~60 行 |
| `useSenderLogic.ts` | 房间创建 + 文件列表同步 | ~150 行 |
| `useReceiverLogic.ts` | 加入房间 + 下载管理 | ~120 行 |
| `useTransferEffects.ts` | 集中管理 useEffect | ~100 行 |
**9 个 `useEffect` 优化为 4 个:**
| 当前 | 优化后 |
|------|--------|
| `onFileListReceived` effect | → 合并到 `useTransferEffects` |
| `onFileReceived` effect | → 合并到 `useTransferEffects` |
| `onFileProgress` effect | → 合并到 `useTransferEffects` |
| `onFileRequested` effect | → 合并到 `useTransferEffects` |
| 错误处理 effect × 2 (重复) | → 删除 1 个,保留 1 个 |
| 连接日志 effect × 2 | → 删除(改用 logger 工具) |
| `selectedFiles` 同步 effect | → 保留,移入 `useSenderLogic` |
---
### Phase 4基础设施清理
> 代码卫生。预估耗时:半天。
| 任务 | 效果 |
|------|------|
| 引入 `logger.ts` 工具dev 输出 / prod 静默),替换 428 个 `console.log` | 清洁生产日志 |
| 把 `setTimeout` 同步替换为事件监听 Promise监听 `connectionstatechange` / `datachannel.open` | 消除脆弱时序 |
| `useTabNavigation` 改为通过回调通知父组件处理断连,不直接依赖 WebRTC | 关注点分离 |
| 清理 `types/index.ts` 中未使用的类型 | 减少死代码 |
| DataChannel `onerror` 提取为共享处理器 | -30 行重复 |
---
## 五、优化总览
| Phase | 目标 | 代码影响 | 难度 | 耗时 |
|-------|------|---------|------|------|
| **Phase 1** | 消除重复 | **-690 行** | ⭐ 低 | 半天 |
| **Phase 2** | 扁平化 Hook 层 5→3 | **-400 行** + 结构简化 | ⭐⭐⭐ 高 | 1-2 天 |
| **Phase 3** | 拆分超级组件 | 0重组织 | ⭐⭐ 中 | 1 天 |
| **Phase 4** | 基础设施 | **-428 行** console.log | ⭐ 低 | 半天 |
**总预估**
- 代码量11,776 行 → ~10,000 行(减少 ~15%
- 抽象层数5 层 → 3 层
- useEffect75 个 → ~50 个
- console.log428 处 → 0替换为 logger
- FileInfo 定义7 处 → 1 处
- 房间验证实现4 处 → 1 处