Files
soul-yongping/开发文档/8、部署/交易中心Tab按需加载优化.md

420 lines
11 KiB
Markdown
Raw Normal View History

# 交易中心 Tab 按需加载优化
## 问题描述
在后台管理的"交易中心"页面(`/admin/distribution`),存在性能问题:
**原问题**
- 每次切换 tab 都会重新请求**所有数据**
- 包括概览数据、用户数据、订单数据、绑定数据、提现数据
- 即使某个 tab 的数据已经加载过,再次切换回来也会重新请求
- 导致不必要的网络请求和数据库查询
**用户反馈**
> 每次切换tab不需要重新请求/api/admin/distribution/overview每个tab的列表数据都不一样切换tab的时候请求tab内对应的内容即可
## 优化目标
1. ✅ 概览数据只在初次加载时请求一次
2. ✅ 用户数据只在初次加载时请求一次(多个 tab 需要用到)
3. ✅ 各 tab 的数据按需加载,切换到该 tab 时才请求
4. ✅ 已加载过的 tab 数据缓存,再次切换不重复请求
5. ✅ 提供刷新功能,可强制重新加载当前 tab 数据
## 解决方案
### 1. 拆分加载逻辑
**修改前**(单一 `loadData` 函数):
```typescript
const loadData = async () => {
setLoading(true)
// 加载概览数据
// 加载用户数据
// 加载订单数据
// 加载绑定数据
// 加载提现数据
setLoading(false)
}
useEffect(() => {
loadData() // ❌ 每次切换tab都执行
}, [activeTab])
```
**修改后**(分离初始化和按需加载):
```typescript
// 1. 初始化加载:概览 + 用户
const loadInitialData = async () => {
// 加载概览数据(/api/admin/distribution/overview
// 加载用户数据(/api/db/users
}
// 2. 按需加载tab数据
const loadTabData = async (tab: string) => {
if (loadedTabs.has(tab)) {
console.log(`${tab} 数据已缓存,跳过加载`)
return // ✅ 已加载过,跳过
}
switch (tab) {
case 'overview': break // 无需额外加载
case 'orders': // 加载订单
case 'bindings': // 加载绑定
case 'withdrawals': // 加载提现
}
setLoadedTabs(prev => new Set(prev).add(tab)) // ✅ 标记已加载
}
// 初次加载
useEffect(() => {
loadInitialData()
}, [])
// tab切换时按需加载
useEffect(() => {
loadTabData(activeTab)
}, [activeTab])
```
### 2. 数据缓存机制
使用 `Set` 记录已加载的 tab
```typescript
const [loadedTabs, setLoadedTabs] = useState<Set<string>>(new Set())
```
**加载流程**
```
用户访问页面
loadInitialData()
├─ 加载概览数据
└─ 加载用户数据
默认显示 overview tab
用户切换到 orders tab
loadTabData('orders')
├─ 检查 loadedTabs.has('orders')? 否
├─ 请求 /api/orders
├─ 更新 loadedTabs.add('orders')
└─ 完成
用户切换回 overview tab
loadTabData('overview')
└─ 检查 loadedTabs.has('overview')? 是,跳过加载
用户切换到 orders tab
loadTabData('orders')
└─ 检查 loadedTabs.has('orders')? 是,跳过加载
```
### 3. 刷新功能
新增 `refreshCurrentTab()` 函数,允许强制重新加载:
```typescript
const refreshCurrentTab = () => {
// 移除当前tab的缓存标记
setLoadedTabs(prev => {
const newSet = new Set(prev)
newSet.delete(activeTab)
return newSet
})
// 重新加载概览数据如果在概览tab
if (activeTab === 'overview') {
loadInitialData()
}
// 重新加载当前tab数据
loadTabData(activeTab)
}
```
**使用场景**
- 点击"刷新数据"按钮
- 审批提现后刷新提现列表
- 修改数据后刷新当前视图
## Tab 数据加载映射
| Tab 名称 | 加载内容 | API 接口 | 何时加载 |
|---------|---------|---------|---------|
| `overview` | 概览统计 | `/api/admin/distribution/overview` | 初始化 |
| `orders` | 订单列表 | `/api/orders` | 切换到订单tab时 |
| `bindings` | 绑定关系 | `/api/db/distribution` | 切换到绑定tab时 |
| `withdrawals` | 提现记录 | `/api/db/withdrawals` | 切换到提现tab时 |
| *全局* | 用户数据 | `/api/db/users` | 初始化多个tab需要 |
## 修改文件清单
**文件路径**`app/admin/distribution/page.tsx`
**修改内容**
1. **新增状态**(第 114 行):
```typescript
const [loadedTabs, setLoadedTabs] = useState<Set<string>>(new Set())
```
2. **修改 useEffect**(第 116-123 行):
```typescript
// 修改前
useEffect(() => {
loadData()
}, [activeTab])
// 修改后
useEffect(() => {
loadInitialData()
}, [])
useEffect(() => {
loadTabData(activeTab)
}, [activeTab])
```
3. **新增 `loadInitialData()` 函数**(第 125-157 行):
- 加载概览数据
- 加载用户数据
4. **新增 `loadTabData()` 函数**(第 159-257 行):
- 检查缓存
- 根据 tab 加载对应数据
- 标记已加载
5. **新增 `refreshCurrentTab()` 函数**(第 259-270 行):
- 清除当前 tab 缓存
- 重新加载数据
6. **修改刷新按钮**(第 375 行):
```typescript
// 修改前
onClick={loadData}
// 修改后
onClick={refreshCurrentTab}
```
7. **修改提现审批回调**(第 285, 300 行):
```typescript
// 修改前
loadData()
// 修改后
refreshCurrentTab()
```
## 性能对比
### 修改前
| 操作 | API 请求数 | 说明 |
|-----|-----------|------|
| 初次访问 | 5 个 | overview, users, orders, bindings, withdrawals |
| 切换到订单 tab | 5 个 | 重复请求所有数据 |
| 切换到绑定 tab | 5 个 | 重复请求所有数据 |
| 切换回概览 tab | 5 个 | 重复请求所有数据 |
| **总计** | **20 个** | 重复请求 4 次 |
### 修改后
| 操作 | API 请求数 | 说明 |
|-----|-----------|------|
| 初次访问概览tab | 2 个 | overview, users |
| 切换到订单 tab | 1 个 | orders首次 |
| 切换到绑定 tab | 1 个 | bindings首次 |
| 切换回概览 tab | 0 个 | ✅ 已缓存,跳过 |
| 切换回订单 tab | 0 个 | ✅ 已缓存,跳过 |
| **总计** | **4 个** | 减少 80% 请求 |
**性能提升**
- ✅ API 请求减少 **80%**
- ✅ 数据库查询减少 **80%**
- ✅ 页面加载速度提升
- ✅ 服务器负载降低
## 验证步骤
### 1. 重启服务
```powershell
pm2 restart mycontent
# 或
npm run dev
```
### 2. 打开开发者工具
访问 `http://localhost:3006/admin/distribution`
按 F12 打开 DevTools → Network 标签 → 勾选"Preserve log"
### 3. 测试加载流程
**步骤 1初次访问**
- 应该看到 2 个请求:
- `/api/admin/distribution/overview`
- `/api/db/users`
- 控制台输出:
```
[Admin] 加载初始数据...
[Admin] 概览数据加载成功
[Admin] 用户数据加载成功
[Admin] overview 数据已缓存,跳过加载
```
**步骤 2切换到"订单管理"tab**
- 应该看到 1 个新请求:
- `/api/orders`
- 控制台输出:
```
[Admin] 加载 orders 数据...
[Admin] 订单数据加载成功: X 条
```
**步骤 3切换到"绑定管理"tab**
- 应该看到 1 个新请求:
- `/api/db/distribution`
- 控制台输出:
```
[Admin] 加载 bindings 数据...
[Admin] 绑定数据加载成功: X 条
```
**步骤 4切换回"数据概览"tab**
-**不应该有任何新请求**
- 控制台输出:
```
[Admin] overview 数据已缓存,跳过加载
```
**步骤 5点击"刷新数据"按钮**
- 应该看到当前 tab 对应的 API 请求
- 控制台输出:
```
[Admin] 加载 overview 数据...
[Admin] 概览数据加载成功
```
### 4. 测试审批功能
1. 切换到"提现审核" tab
2. 批准或拒绝一条提现记录
3. 应该只刷新提现数据1 个请求)
4. 不应该重新请求概览、用户、订单、绑定数据
## 注意事项
### 1. 用户数据全局共享
用户数据在初始化时加载一次,供所有 tab 使用:
- 订单管理需要:显示用户昵称、手机号
- 绑定管理需要:显示推荐人和被推荐人信息
- 提现审核需要:显示用户信息
### 2. 概览数据不自动更新
概览数据在初始化时加载,切换 tab 不会更新。如果需要最新的统计数据:
- 点击"刷新数据"按钮
- 或刷新整个页面F5
**未来优化**:可以考虑轮询或 WebSocket 实时更新概览数据。
### 3. 缓存策略
当前缓存策略:
- ✅ 同一页面会话内缓存(刷新页面会清空)
- ✅ 切换 tab 时保留缓存
- ✅ 点击"刷新数据"清空当前 tab 缓存
**未来优化**
- 可以设置缓存过期时间(如 5 分钟)
- 可以使用 SWR 或 React Query 管理缓存
### 4. 概览 tab 的特殊性
`overview` tab 在第一次 `loadTabData('overview')` 时会命中缓存判断并跳过,因为:
1. 初始化时会标记 `overview` 为已加载(虽然实际是在 `loadInitialData` 中加载的)
2. 这是合理的,因为概览数据已经在初始化时加载了
**实现建议**如果希望概览tab也能独立刷新可以在 `loadInitialData` 中不标记 `overview`
```typescript
// 不推荐因为会导致切换到overview tab时重复请求
setLoadedTabs(prev => new Set(prev).add('overview'))
```
## 扩展功能
### 1. 自动刷新
可以为特定 tab 添加自动刷新:
```typescript
useEffect(() => {
if (activeTab === 'withdrawals') {
const interval = setInterval(() => {
refreshCurrentTab()
}, 30000) // 每 30 秒刷新一次提现数据
return () => clearInterval(interval)
}
}, [activeTab])
```
### 2. 缓存过期时间
```typescript
const [loadedTabs, setLoadedTabs] = useState<Map<string, number>>(new Map())
const loadTabData = async (tab: string) => {
const lastLoaded = loadedTabs.get(tab)
const now = Date.now()
// 5分钟内的缓存有效
if (lastLoaded && (now - lastLoaded) < 5 * 60 * 1000) {
console.log(`${tab} 缓存有效,跳过加载`)
return
}
// 加载数据...
setLoadedTabs(prev => new Map(prev).set(tab, now))
}
```
### 3. 全局刷新
```typescript
const refreshAll = () => {
setLoadedTabs(new Set()) // 清空所有缓存
loadInitialData() // 重新加载初始数据
loadTabData(activeTab) // 重新加载当前tab
}
```
## 相关文件
- **交易中心页面**`app/admin/distribution/page.tsx`
- **概览API**`app/api/admin/distribution/overview/route.ts`
- **用户API**`app/api/db/users/route.ts`
- **订单API**`app/api/orders/route.ts`
- **绑定API**`app/api/db/distribution/route.ts`
- **提现API**`app/api/db/withdrawals/route.ts`
## 版本信息
- **优化时间**2026-02-04
- **优化内容**
1. 拆分 `loadData``loadInitialData``loadTabData`
2. 实现数据缓存机制(`loadedTabs` Set
3. 按需加载各 tab 数据
4. 新增 `refreshCurrentTab` 刷新功能
5. 减少 80% 的 API 请求和数据库查询