简单生活

我基于PeerJS搭建了一个P2P视频软件

一个拖了很久的待办

"玩一玩WebRTC" 在我的待办清单里躺了大概一年,因为这事有趣,但是项目太多无法抽身,最近vibe coding大行其道,这种小demo的项目,可以快速实现,大概花了2天时间实现了这个旧项目:Peertest

WebRTC、PeerJS、视频通话的关系

在聊实现之前,先理清这几个概念的关系。

WebRTC:底层的通信协议

WebRTC (Web Real-Time Communication) 是浏览器原生的实时通信 API。它可以让两个浏览器直接传输音视频数据。

WebRTC 只负责"传输",不负责"建立连接"。

打个比方,WebRTC 就像是两个人打电话的"通话线路",但在此之前,你们得先交换电话号码。这个"交换号码"的过程,叫信令(Signaling)

信令服务器:连接的桥梁

两个浏览器要建立 WebRTC 连接,需要先交换一些信息:

这些信息需要通过一个中间服务器来传递,这就是信令服务器。

而这就是 PeerJS 存在的意义。

它帮你:

原本需要几百行代码的 WebRTC 连接,用 PeerJS 几行就能搞定。

所以关系是这样的:

视频通话应用
    ↓
PeerJS (封装层)
    ↓
WebRTC (通信协议) + PeerServer (信令服务)

我的选型

前端:React + TypeScript + Vite

选择 React 是因为熟悉,TypeScript 是为了类型安全,Vite 则是因为开发体验好(真的很快)。

整个应用只有一个 App.tsx 组件,单组件架构。我觉得这个规模的项目,没必要过度设计。

核心设计:主叫方(房间建立者)和被叫方(加入房间这)

建立房间模式

用户点击"建立房间"
    → 连接 PeerServer,获取 6 位数字房间号
    → 生成分享链接 http://host:port/#123456
    → 等待对方来电

加入房间模式

用户粘贴分享链接,点击"呼叫"
    → 自动连接 PeerServer
    → 同时发起 WebRTC 呼叫

单组件的状态管理

没有用 Redux、Zustand 任何状态库,纯 React hooks:

const [mode, setMode] = useState<'join' | 'create'>('join')
const [connectionStatus, setConnectionStatus] = useState<'disconnected' | 'connecting' | 'connected'>('disconnected')
const [callStatus, setCallStatus] = useState<'idle' | 'calling' | 'incall'>('idle')

我觉得这就是"刚刚好"的复杂度——再多就是过度设计。

服务器架构

两个独立的服务:

  1. PeerServer (peer-server.js):Node.js 写的信令服务器,端口 9009
  2. Vite Dev Server:前端开发服务器

配置 host: '0.0.0.0' 是为了支持局域网访问——这样我的手机可以和电脑互相通话。

一个小细节:6 位数字房间号

我用了随机生成 6 位数字作为房间 ID:

const generateRoomId = (): string => {
  return Math.floor(100000 + Math.random() * 900000).toString()
}

为什么不用 UUID?因为数字好记、好输入,特别是手机端粘贴链接时体验更好。

摄像头控制

每个视频框都有独立的开关:

本地视频加了镜像翻转 transform: scaleX(-1),不然看着自己的视频会很别扭。

技术栈总结

层级 技术
前端框架 React 19 + TypeScript
构建工具 Vite
WebRTC 封装 PeerJS 1.5.5
信令服务器 PeerServer (peer 包)
状态管理 React hooks

再见

写到这里,代码已经能跑了。两个浏览器窗口可以互相通话,画质清晰,延迟低。

这个项目不大,但填补了我对 WebRTC 的认知空白。

对了,代码在 Gitee,欢迎 star。


再见,下一个待办见。