Files
JKVideo/CLAUDE.md
Developer 18eebfb0d2 1
2026-03-10 17:25:18 +08:00

120 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-iconsIonicons |
## 目录结构
```
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
- `video` Tab 在导航栏中隐藏(`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 间隔) |
### 播放流参数(重要)
```ts
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 buildnative 编译),在 Expo Go 中报 `Cannot find native module 'ExponentAV'`
- `react-native-webview` 在 Expo Go 中开箱即用
-`JSON.stringify(uri)` 注入 src防止 URL 中 `&``=` 等特殊字符破坏 HTML 属性
## 登录流程
1. 首页右上角点击头像图标 → 弹出 `LoginModal`
2. 调用 `generateQRCode()` 获取 `qrcode_key` + 二维码 URL
3.`https://api.qrserver.com` 渲染二维码图片(第三方服务)
4. 每 2s 轮询 `pollQRCode``code === 0` 时从响应 Header `set-cookie` 提取 `SESSDATA`
5. `useAuthStore.login()``SESSDATA` 写入 AsyncStorage 并更新 Zustand 状态
6. 下次启动时 `_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**`buvid3` Cookie 或 `SESSDATA` 失效,清除 AsyncStorage 重试
- **评论为空**B 站部分视频评论区关闭,`replies` 字段为 null 属正常
- **二维码过期**`pollQRCode` 返回 `code === 86038`,关闭 Modal 重新打开即可
### 扩展方向
- 搜索功能:`/x/web-interface/search/all/v2`
- 动态流:需更高权限 Cookie`bili_jct` CSRF Token
- DASH 播放:需解析 MPD配合支持 DASH 的播放器(如 `react-native-video` + dev build
- 弹幕:`/x/v1/dm/list.so?oid={cid}` 返回 XML 格式