mirror of
https://github.com/MatrixSeven/file-transfer-go.git
synced 2026-03-17 13:57:41 +08:00
472 lines
23 KiB
Markdown
472 lines
23 KiB
Markdown
# 前端代码架构分析 & 优化计划
|
||
|
||
> 最后更新: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` 同时导出 hook(React 组件用)和独立函数 `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 | 连接状态 UI(3 种模式: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 行,使用 prop),426 行总计 |
|
||
| **两套 API 封装并存** | 🟡 中 | `api-utils.ts`(函数式)+ `client-api.ts`(class-based),263 行,但组件大多直接用 `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 层
|
||
- useEffect:75 个 → ~50 个
|
||
- console.log:428 处 → 0(替换为 logger)
|
||
- FileInfo 定义:7 处 → 1 处
|
||
- 房间验证实现:4 处 → 1 处
|