mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-07 23:18:38 +08:00
5.1 KiB
5.1 KiB
reactBilibiliApp — 项目架构与技术要点
项目概述
仿 B 站 React Native 客户端,使用 Expo SDK 55 + expo-router,调用 Bilibili 官方 Web API 获取热门视频、视频详情、评论及扫码登录。
技术栈
| 层 | 技术 |
|---|---|
| 框架 | React Native 0.83 + Expo SDK 55 |
| 路由 | expo-router v4(文件系统路由) |
| 状态管理 | Zustand |
| 网络请求 | Axios |
| 本地存储 | @react-native-async-storage/async-storage |
| 视频播放 | react-native-webview(内嵌 HTML5 video) |
| 图标 | @expo/vector-icons(Ionicons) |
目录结构
app/
_layout.tsx # 根布局:Tab 导航 + 启动时恢复登录态
index.tsx # 首页:热门视频瀑布流(双列 FlatList)
video/
_layout.tsx # Stack 导航(无头部)
[bvid].tsx # 视频详情页(播放 + 简介 + 评论)
components/
VideoCard.tsx # 视频卡片(封面、标题、UP主、播放量)
VideoPlayer.tsx # 视频播放器入口:web 用 <video>,native 用 NativeVideoPlayer
NativeVideoPlayer.tsx# WebView 内嵌 HTML5 video 播放,用 JS 注入 src 避免 URL 特殊字符问题
CommentItem.tsx # 评论条目
LoginModal.tsx # 扫码登录 Modal(底部弹出)
hooks/
useVideoList.ts # 热门视频分页加载(支持下拉刷新、上拉加载更多)
useVideoDetail.ts # 视频详情 + 获取播放流 URL
useComments.ts # 评论分页加载
services/
bilibili.ts # 所有 API 请求(axios 实例 + Cookie 拦截器)
types.ts # 数据类型定义
store/
authStore.ts # Zustand 登录状态(sessdata/uid/username + 持久化)
utils/
format.ts # formatCount / formatDuration / formatTime
路由结构
/→ 首页(Tab)/video/[bvid]→ 视频详情(Stack,从 Tab 内 push)videoTab 在导航栏中隐藏(href: null)
Bilibili API 关键点
axios 实例(services/bilibili.ts)
- 统一携带
Referer: https://www.bilibili.com、User-Agent(模拟 Chrome) - 请求拦截器自动注入 Cookie:
buvid3(随机生成并持久化)+SESSDATA(登录后)
主要接口
| 函数 | 接口 | 说明 |
|---|---|---|
getPopularVideos(pn) |
/x/web-interface/popular |
热门视频,每页 20 条 |
getVideoDetail(bvid) |
/x/web-interface/view |
视频详情 |
getPlayUrl(bvid, cid) |
/x/player/playurl |
获取播放流 |
getComments(aid, pn) |
/x/v2/reply |
评论列表,sort=2(热评) |
generateQRCode() |
passport 域名 | 生成登录二维码 |
pollQRCode(key) |
passport 域名 | 轮询扫码状态(2s 间隔) |
播放流参数(重要)
params: { bvid, cid, qn: 64, fnval: 0, platform: 'html5' }
fnval: 0+platform: 'html5'→ 返回 MP4 格式(durl[0].url)fnval: 1→ FLV,不可用(HTML5 video 不支持,WebView 无法播放)fnval: 16→ DASH,结构不同(dash字段,非durl),需单独实现qn: 64= 720P,未登录时 B 站可能降级到较低清晰度
视频播放架构
VideoPlayer
├── web (Platform.OS === 'web') → 原生 <video> 标签
└── native → NativeVideoPlayer (WebView)
└── HTML 模板 + JS 注入 src
document.getElementById('v').src = JSON.stringify(uri)
为什么用 WebView 而非 expo-av:
expo-av需要 development build(native 编译),在 Expo Go 中报Cannot find native module 'ExponentAV'react-native-webview在 Expo Go 中开箱即用- 用
JSON.stringify(uri)注入 src,防止 URL 中&、=等特殊字符破坏 HTML 属性
登录流程
- 首页右上角点击头像图标 → 弹出
LoginModal - 调用
generateQRCode()获取qrcode_key+ 二维码 URL - 用
https://api.qrserver.com渲染二维码图片(第三方服务) - 每 2s 轮询
pollQRCode,code === 0时从响应 Headerset-cookie提取SESSDATA useAuthStore.login()将SESSDATA写入 AsyncStorage 并更新 Zustand 状态- 下次启动时
_layout.tsx调用restore()恢复登录态
主题色
- 主色:
#00AEEC(B 站蓝) - 文字主色:
#212121 - 次要文字:
#999 - 背景:
#f4f4f4(列表)/#fff(卡片)
开发注意事项
运行方式
- Expo Go:
expo start扫码,视频播放用 WebView 方案 - Dev Build:
expo run:android,可使用更多 native 模块
常见问题
- 视频无法播放:检查
fnval是否为0,确认返回的是 MP4 而非 FLV - API 403:
buvid3Cookie 或SESSDATA失效,清除 AsyncStorage 重试 - 评论为空:B 站部分视频评论区关闭,
replies字段为 null 属正常 - 二维码过期:
pollQRCode返回code === 86038,关闭 Modal 重新打开即可
扩展方向
- 搜索功能:
/x/web-interface/search/all/v2 - 动态流:需更高权限 Cookie(
bili_jctCSRF Token) - DASH 播放:需解析 MPD,配合支持 DASH 的播放器(如
react-native-video+ dev build) - 弹幕:
/x/v1/dm/list.so?oid={cid}返回 XML 格式