Files
file-transfer-go/FRONTEND_ANALYSIS.md

23 KiB
Raw Blame History

前端代码架构分析 & 优化计划

最后更新2026-02-28


目录


一、全局数据

指标 数值
总行数 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-synctext-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/viewsend/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 桌面共享接收方(含重复的房间验证逻辑

复杂度观察:

  • WebRTCTextSenderWebRTCTextReceiver 各自独立调用 useSharedWebRTCManager(),然后创建 useTextTransferBusiness(connection)useFileTransferBusiness(connection)。这意味着每个子组件都创建了自己的独立 WebRTC 连接实例
  • WebRTCDesktopReceiver 中有两处几乎完全相同的房间验证逻辑(handleJoinViewingautoJoin 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 微信群二维码页

ConnectionStatusWebRTCConnectionStatus两个独立的连接状态组件,功能高度重叠但接口不同:前者使用 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.ts51 行)— 定义了 FileInfo, TransferProgress, RoomStatus, FileChunk, WebSocketMessage, UseWebSocketReturn

问题: FileInfo 类型在此文件中定义了一次,但在 WebRTCFileTransfer.tsxWebRTCFileUpload.tsxWebRTCFileReceive.tsxuseFileTransferBusiness.tsuseFileStateManager.tsuseFileListSync.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 ClientAPIOOP style API 封装)
lib/webrtc-support.ts 162 WebRTC 特性检测 + 浏览器信息
lib/utils.ts 6 cn() — tailwind 合并工具
lib/static-config.ts 35 静态/动态页面路由定义

问题: api-utils.tsclient-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 个 HookuseSharedWebRTCManager, useFileTransferBusiness, useFileListSync, useFileStateManager, useRoomConnection, useURLHandler, useConnectionState
  • 9 个 useEffect:多个 effect 监听重叠的状态(isConnected, isConnecting, error),一次状态变化触发多个 effect 连锁执行
  • useConnectionState 完全重复的错误处理

WebRTCFileTransfer.tsx 第 316-368 行的错误处理 if/else 链:

// 组件内的错误处理 (L316-L368)
if (error.includes('WebSocket')) {
  errorMessage = '服务器连接失败...';
} else if (error.includes('数据通道')) { ... }

useConnectionState.ts 第 46-64 行完全相同

// 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 前缀(如 🔧🚀📤),无级别控制,生产环境产生大量噪音
两个功能重叠的连接状态组件 🟡 ConnectionStatus241 行,使用 zustand+ WebRTCConnectionStatus185 行,使用 prop426 行总计
两套 API 封装并存 🟡 api-utils.ts(函数式)+ client-api.tsclass-based263 行,但组件大多直接用 fetch()
types/index.ts 中的死代码 🟢 UseWebSocketReturn 等类型无人引用
28 处 setTimeout 作为同步机制 🟡 500ms/2000ms 硬编码等待连接稳定,在低性能设备上可能不够,在高性能设备上浪费时间
useWebRTCTrackManager.onTrack 轮询 🟢 50 次 × 100ms 轮询等待轨道就绪,应改为事件驱动
DataChannel onerror 处理重复 🟡 sender 和 receiver 分支中约 30 行完全相同的 switch 语句

四、优化计划

Phase 1消除重复、统一类型

影响大,改动小。预估耗时:半天。

任务 预估时间 效果
统一 FileInfo(含 status/progresstypes/index.ts,删除 7 处重复定义 30min -60 行,消除类型漂移
抽取 validateRoom(code) 通用函数,替代 4 处房间验证 30min -200 行
合并 ConnectionStatus + WebRTCConnectionStatus 为一个组件 1h -150 行
删除 useConnectionState hook其逻辑已在组件中重复 15min -138 行,修复双重 Toast
删除 api-utils.tsclient-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. 删除 useWebRTCStateManager77 行)

    • 直接在 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.tsx658 行)→ 拆分为:

新文件 职责 预估行数
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 处