You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React JS与expo-cli版React Native跨端视频通话方案咨询

可行性结论

React JS Web端 + Expo 搭建的 React Native 端实现跨端视频通话完全可落地,核心基于WebRTC协议实现,两端均有成熟的适配库支持,不需要替换现有技术栈。

原生WebRTC实现方案(无强绑定第三方付费服务)

这个方案自由度最高,所有逻辑可控,核心分为三部分:信令服务、Web端实现、Expo RN端实现。

前置说明

WebRTC本身不需要服务端中转音视频流,但需要一个轻量信令服务做连接前的信息交换(交换SDP协商信息、ICE网络候选地址),信令服务不处理音视频数据,压力极小。

1. 最简信令服务实现

你可以用Node.js搭一个仅几十行代码的WebSocket服务做信令,双人通话场景下直接做消息广播即可:

// 信令服务代码,基于ws库,先执行 npm i ws 安装依赖
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });
const connectedClients = new Set();

wss.on('connection', (client) => {
  connectedClients.add(client);
  // 收到任意客户端的消息,直接转发给其他所有在线客户端
  client.on('message', (payload) => {
    connectedClients.forEach(peer => {
      if (peer !== client && peer.readyState === 1) {
        peer.send(payload.toString());
      }
    });
  });
  client.on('close', () => connectedClients.delete(client));
});

本地测试直接node 文件名.js启动即可,生产环境记得加wss加密、简单的房间逻辑区分不同通话组。

2. React Web端实现

Web端直接用浏览器原生WebRTC API即可,不需要额外安装重型依赖,核心代码如下:

import { useEffect, useRef } from 'react';

export default function VideoCall() {
  const localVideoRef = useRef(null);
  const remoteVideoRef = useRef(null);
  const peerConnection = useRef(null);
  const ws = useRef(new WebSocket('ws://127.0.0.1:8080')); // 填你的信令服务地址

  useEffect(() => {
    const initCall = async () => {
      // 获取本地摄像头、麦克风权限
      const localStream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: 'user' },
        audio: true
      });
      localVideoRef.current.srcObject = localStream;

      // 初始化点对点连接,配置公共STUN服务器做网络探测
      peerConnection.current = new RTCPeerConnection({
        iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
      });

      // 将本地音视频轨加入连接
      localStream.getTracks().forEach(track => {
        peerConnection.current.addTrack(track, localStream);
      });

      // 收到对端媒体流时,绑定到远端视频元素
      peerConnection.current.ontrack = (event) => {
        remoteVideoRef.current.srcObject = event.streams[0];
      };

      // 收集到本地网络候选地址时,通过信令发给对端
      peerConnection.current.onicecandidate = (event) => {
        if (event.candidate) {
          ws.current.send(JSON.stringify({
            type: 'ice-candidate',
            payload: event.candidate
          }));
        }
      };
    };

    initCall();

    // 监听信令消息,处理对端发来的协商信息
    ws.current.onmessage = async (event) => {
      const message = JSON.parse(event.data);
      switch (message.type) {
        case 'offer':
          await peerConnection.current.setRemoteDescription(new RTCSessionDescription(message.payload));
          const answer = await peerConnection.current.createAnswer();
          await peerConnection.current.setLocalDescription(answer);
          ws.current.send(JSON.stringify({ type: 'answer', payload: answer }));
          break;
        case 'answer':
          await peerConnection.current.setRemoteDescription(new RTCSessionDescription(message.payload));
          break;
        case 'ice-candidate':
          await peerConnection.current.addIceCandidate(new RTCIceCandidate(message.payload));
          break;
      }
    };
  }, []);

  // 主动发起通话
  const handleStartCall = async () => {
    const offer = await peerConnection.current.createOffer();
    await peerConnection.current.setLocalDescription(offer);
    ws.current.send(JSON.stringify({ type: 'offer', payload: offer }));
  };

  return (
    <div className="video-container">
      <video ref={localVideoRef} autoPlay muted style={{ width: 160, height: 240 }} />
      <video ref={remoteVideoRef} autoPlay style={{ width: 480, height: 320 }} />
      <button onClick={handleStartCall}>发起通话</button>
    </div>
  );
}

3. Expo React Native端实现

前置配置

Expo Go不包含WebRTC相关原生模块,必须打包自定义开发客户端才能运行,步骤如下:

  1. 安装依赖:
    npx expo install react-native-webrtc @config-plugins/react-native-webrtc
  2. 修改app.json,添加插件配置和权限声明:
{
  "expo": {
    "plugins": ["@config-plugins/react-native-webrtc"],
    "ios": {
      "infoPlist": {
        "NSCameraUsageDescription": "需要摄像头权限进行视频通话",
        "NSMicrophoneUsageDescription": "需要麦克风权限进行视频通话"
      }
    },
    "android": {
      "permissions": ["CAMERA", "RECORD_AUDIO"]
    }
  }
}
  1. 执行npx expo prebuild生成原生工程,再执行npx expo run:android或npx expo run:ios启动自定义开发客户端即可。

RN端核心代码

信令处理逻辑和Web端完全一致,仅媒体采集、视频渲染用RN端适配的API:

import { useEffect, useRef, useState } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import { mediaDevices, RTCPeerConnection, RTCView } from 'react-native-webrtc';

export default function VideoCallScreen() {
  const [localStream, setLocalStream] = useState(null);
  const [remoteStream, setRemoteStream] = useState(null);
  const peerConnection = useRef(null);
  const ws = useRef(new WebSocket('ws://127.0.0.1:8080')); // 和Web端填同一个信令地址

  useEffect(() => {
    const initCall = async () => {
      // 申请权限并获取本地音视频流
      const stream = await mediaDevices.getUserMedia({
        video: { facingMode: 'user' },
        audio: true
      });
      setLocalStream(stream);

      // 初始化点对点连接,iceServers配置必须和Web端完全一致
      peerConnection.current = new RTCPeerConnection({
        iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
      });

      stream.getTracks().forEach(track => {
        peerConnection.current.addTrack(track, stream);
      });

      peerConnection.current.ontrack = (event) => {
        setRemoteStream(event.streams[0]);
      };

      peerConnection.current.onicecandidate = (event) => {
        if (event.candidate) {
          ws.current.send(JSON.stringify({
            type: 'ice-candidate',
            payload: event.candidate
          }));
        }
      };
    };

    initCall();

    // 信令处理逻辑和Web端完全相同
    ws.current.onmessage = async (event) => {
      const message = JSON.parse(event.data);
      switch (message.type) {
        case 'offer':
          await peerConnection.current.setRemoteDescription(new RTCSessionDescription(message.payload));
          const answer = await peerConnection.current.createAnswer();
          await peerConnection.current.setLocalDescription(answer);
          ws.current.send(JSON.stringify({ type: 'answer', payload: answer }));
          break;
        case 'answer':
          await peerConnection.current.setRemoteDescription(new RTCSessionDescription(message.payload));
          break;
        case 'ice-candidate':
          await peerConnection.current.addIceCandidate(new RTCIceCandidate(message.payload));
          break;
      }
    };
  }, []);

  const handleStartCall = async () => {
    const offer = await peerConnection.current.createOffer();
    await peerConnection.current.setLocalDescription(offer);
    ws.current.send(JSON.stringify({ type: 'offer', payload: offer }));
  };

  return (
    <View style={styles.container}>
      {localStream && <RTCView streamURL={localStream.toURL()} style={styles.localVideo} />}
      {remoteStream && <RTCView streamURL={remoteStream.toURL()} style={styles.remoteVideo} />}
      <Button title="发起通话" onPress={handleStartCall} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 50 },
  localVideo: { width: 120, height: 180, position: 'absolute', top: 60, right: 20, zIndex: 10 },
  remoteVideo: { width: '100%', height: 300 }
});
替代方案(降低开发成本)

如果不想自己处理WebRTC的NAT穿透、弱网优化、流控这些逻辑,可以选以下方案:

  • 用成熟的商用RTC SDK:比如声网Agora、腾讯实时音视频TRTC,都提供官方的React Web SDK和Expo适配的React Native SDK,按文档接入最快1小时就能跑通功能,免费额度足够小产品初期使用,超出后按用量付费
  • 用封装好的WebRTC库:比如PeerJS,Web端直接引入npm包,RN端用react-native-peerjs适配,自带公共信令服务器,测试场景不用自己搭服务,生产环境可以自己部署PeerServer做信令
常见踩坑提醒
  • 不要尝试在Expo Go里调试WebRTC相关功能,Expo Go没有打包对应原生模块,跑不起来,必须用自定义dev客户端
  • 公共STUN服务器仅能用于本地测试,生产环境必须部署TURN服务器,不然跨内网的客户端大概率无法连通
  • 两端RTCPeerConnection的iceServers配置必须完全一致,否则会出现协商失败、连不通的问题
  • React Native端只能用react-native-webrtc提供的RTCView组件渲染视频流,用普通Video组件无法播放

内容的提问来源于stack exchange,提问作者Antonio Rodríguez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:54:20