Files
soul-yongping/开发文档/8、部署/交易中心Tab按需加载优化.md
2026-02-09 15:09:29 +08:00

11 KiB
Raw Blame 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 函数):

const loadData = async () => {
  setLoading(true)
  // 加载概览数据
  // 加载用户数据
  // 加载订单数据
  // 加载绑定数据
  // 加载提现数据
  setLoading(false)
}

useEffect(() => {
  loadData()  // ❌ 每次切换tab都执行
}, [activeTab])

修改后(分离初始化和按需加载):

// 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

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() 函数,允许强制重新加载:

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 行):

    const [loadedTabs, setLoadedTabs] = useState<Set<string>>(new Set())
    
  2. 修改 useEffect(第 116-123 行):

    // 修改前
    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 行):

    // 修改前
    onClick={loadData}
    
    // 修改后
    onClick={refreshCurrentTab}
    
  7. 修改提现审批回调(第 285, 300 行):

    // 修改前
    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. 重启服务

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

// 不推荐因为会导致切换到overview tab时重复请求
setLoadedTabs(prev => new Set(prev).add('overview'))

扩展功能

1. 自动刷新

可以为特定 tab 添加自动刷新:

useEffect(() => {
  if (activeTab === 'withdrawals') {
    const interval = setInterval(() => {
      refreshCurrentTab()
    }, 30000)  // 每 30 秒刷新一次提现数据
    
    return () => clearInterval(interval)
  }
}, [activeTab])

2. 缓存过期时间

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. 全局刷新

const refreshAll = () => {
  setLoadedTabs(new Set())  // 清空所有缓存
  loadInitialData()  // 重新加载初始数据
  loadTabData(activeTab)  // 重新加载当前tab
}

相关文件

  • 交易中心页面app/admin/distribution/page.tsx
  • 概览APIapp/api/admin/distribution/overview/route.ts
  • 用户APIapp/api/db/users/route.ts
  • 订单APIapp/api/orders/route.ts
  • 绑定APIapp/api/db/distribution/route.ts
  • 提现APIapp/api/db/withdrawals/route.ts

版本信息

  • 优化时间2026-02-04
  • 优化内容
    1. 拆分 loadDataloadInitialDataloadTabData
    2. 实现数据缓存机制(loadedTabs Set
    3. 按需加载各 tab 数据
    4. 新增 refreshCurrentTab 刷新功能
    5. 减少 80% 的 API 请求和数据库查询