前言:为什么要做这个项目?
你是否也有过这种困扰?玩云顶之弈想学习不同主播的阵容,打开 5 个浏览器标签页,切来切去错过关键操作😑;想看比赛多路解说,来回切换直播间心态爆炸…🙄与其忍受标签页地狱,不如自己动手!🛠️今天用 React+TypeScript+Node.js,花 1 小时搭建一个「多路直播监控工具」,支持虎牙 / 斗鱼平台,同屏观看、自由静音、灵活布局,彻底解决多直播同时看的痛点~👍️
这个项目不仅能解决了实际需求,还成为了学习React的绝佳实践。今天就来分享这个项目的完整开发过程。
你能学到什么
✅️ 虎牙/斗鱼直播源解析
✅️ 多布局切换
✅️ 本地快速部署
✅️ 完整源码
核心功能实现
1. 直播流卡片组件设计
// StreamTile.tsx - 直播流卡片组件
interface StreamTileProps {
stream: StreamSource;
onRemove: (id: string) => void;
onToggleMute: (id: string) => void;
}
const StreamTile: React.FC<StreamTileProps> = ({
stream,
onRemove,
onToggleMute
}) => {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
return (
<div className="stream-tile">
{/* 视频播放区域 */}
<video
src={stream.url}
muted={stream.isMuted}
onLoadedData={() => setIsLoading(false)}
onError={() => setHasError(true)}
/>
{/* 控制工具栏 */}
<div className="controls">
<button onClick={() => onToggleMute(stream.id)}>
{stream.isMuted ? '🔇' : '🔊'}
</button>
<button onClick={() => onRemove(stream.id)}>❌</button>
</div>
</div>
);
};
2. 多布局网格系统
// 布局枚举定义
export enum GridLayout {
Single = '1x1',
Dual = '2x1',
Triple = '3x1',
Quad = '2x2',
Hex = '3x2'
}
// 动态网格类名生成
const getGridClass = (layout: GridLayout): string => {
const gridMap = {
[GridLayout.Single]: 'grid-cols-1 grid-rows-1',
[GridLayout.Dual]: 'grid-cols-2 grid-rows-1',
[GridLayout.Triple]: 'grid-cols-3 grid-rows-1',
[GridLayout.Quad]: 'grid-cols-2 grid-rows-2',
[GridLayout.Hex]: 'grid-cols-3 grid-rows-2'
};
return gridMap[layout];
};
3. 多平台直播源智能处理
❓ 问题:如何将用户输入的直播平台URL(如虎牙、斗鱼直播间链接)转换为可直接播放的流媒体地址?
- 不同平台(虎牙、斗鱼)有不同的解析规则和API接口
- 需要处理多种流媒体格式(FLV、HLS/m3u8)
- 需要提供多种链接选项以应对不同网络环境
💡 思路:分层解析与优先级策略
🔧 核心代码:
智能识别与分流
// 第一步:判断是否为可直接播放的链接
const isDirectStream = customUrl.includes('.m3u8') ||
customUrl.includes('.flv') ||
customUrl.includes('flvjs');
如果是直接可播放的链接(包含.m3u8、.flv等),直接使用;否则调用后端API解析。
后端 API 解析
// 调用本地解析服务
const response = await fetch('http://localhost:3001/api/parse-url', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ url: customUrl }),
});
将复杂的解析逻辑放在后端,前端只负责展示和选择。
多层级链接优先级策略
按优先级从高到低收集候选链接:
第一优先级:虎牙格式链接
- FLV格式链接(高清、稳定)
- HLS格式链接(兼容性好)
第二优先级:斗鱼格式链接
- PC端链接
- 移动端链接
第三优先级:CDN链接
- m3u8 CDN链接
- FLV CDN链接
第四优先级:其他链接
// 链接优先级收集器
const urlCandidates: string[] = [];
// 虎牙FLV链接(最高优先级)
if (parsedData.links?.flv && typeof parsedData.links.flv === 'object') {
Object.values(parsedData.links.flv).forEach(url => {
if (typeof url === 'string') urlCandidates.push(url);
});
}
// 虎牙HLS链接
if (parsedData.links?.hls && typeof parsedData.links.hls === 'object') {
Object.values(parsedData.links.hls).forEach(url => {
if (typeof url === 'string') urlCandidates.push(url);
});
}
// 斗鱼PC链接
if (parsedData.links?.pc && typeof parsedData.links.pc === 'string') {
urlCandidates.push(parsedData.links.pc);
}
// CDN链接(网络优化)
if (parsedData.links?.cdnLinks?.m3u8 && Array.isArray(parsedData.links.cdnLinks.m3u8)) {
parsedData.links.cdnLinks.m3u8.forEach(url => {
if (typeof url === 'string') urlCandidates.push(url);
});
}
按优先级顺序选择第一个可用链接,确保最佳播放体验。
技术难点与解决方案
难点:FLV格式直播流播放
核心难点:HTML5不支持FLV格式,怎么播放直播流?
❓ 问题:虎牙/斗鱼等平台的直播流多为FLV格式,而原生video标签只支持MP4/HLV,直接播放会报错。
💡 思路:使用flv.js库解析FLV流,将其转成video标签可识别的格式,同时处理加载失败、卡顿等异常场景。
🔧 核心代码:
import flvjs from 'flv.js';
const StreamPlayer = ({ url }) => {
const videoRef = useRef<HTMLVideoElement>(null);
const playerRef = useRef<flvjs.Player | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// 1. 检查浏览器是否支持flv.js
if (!flvjs.isSupported() || !videoRef.current) return;
// 2. 创建播放器实例(关键配置:标记为直播流,启用懒加载)
playerRef.current = flvjs.createPlayer(
{ type: 'flv', url, isLive: true },
{ lazyLoad: true, seekType: 'range' }
);
// 3. 绑定DOM+加载播放
playerRef.current.attachMediaElement(videoRef.current);
playerRef.current.load();
playerRef.current.play().catch(err => console.log('播放失败:', err));
// 4. 监听状态,更新UI
playerRef.current.on(flvjs.Events.LOADING_COMPLETE, () => setIsLoading(false));
// 5. 组件卸载时销毁播放器(避免内存泄漏)
return () => playerRef.current?.destroy();
}, [url]);
return (
<div className="stream-player">
{isLoading && <div className="loading">加载中...</div>}
<video ref={videoRef} controls={false} />
</div>
);
。
⚠️ 避坑点:
- 开发环境跨域:后端需要配置CORS,允许前端请求;
- 部分浏览器不支持Web Worker:设置enableWorker: false提高兼容性;
- 直播流中断处理:可添加重连逻辑(setTimeout重试load())
快速上手
1. 克隆源码
git clone https://github.com/Schuyler2025/ominiview-monitor.git
cd ominiview-monitor
2. 启动前端(React)
npm install # 安装依赖
npm run build
npm run preview
3. 启动后端(Node.js)
cd server
npm install
npm start # 启动服务
使用方法:
- 打开http://localhost:4173;
- 复制虎牙/斗鱼主播直播间链接(比如www.huya.com/xxx);
- 粘贴到输入框,点击“添加直播”;
- 右上角切换布局,点击直播卡片的🔇/❌控制静音/关闭。
总结与展望
通过这个项目,不仅掌握了React的核心概念(组件化、状态管理、生命周期),还深入理解了现代前端工程化实践。
为什么这个项目值得学?
解决真实痛点:不是为了学 React 而写 Demo,而是真的能替代 “多标签页切换”;
技术栈实用:React Hooks+TypeScript+Node.js,都是前端面试高频技术;
技术收获:
- React Hooks的熟练使用
- TypeScript类型系统设计
- 工程化构建配置
未来规划:
- 支持更多直播平台(B站、抖音等)
- 添加录制功能
- 开发浏览器插件版本
给新手的建议
- 从实际需求出发:解决真实问题能让学习更有动力
- 循序渐进:先实现核心功能,再逐步优化
- 善用工具:gemini3、豆包等工具能提升开发效率
- 代码规范:从一开始就养成良好的编码习惯
源码地址
项目已开源,欢迎Star和贡献: 👉 GitHub仓库地址
如果文章对你有帮助,欢迎点赞收藏~有任何问题可以在评论区交流!