docs: 扩充 AGENTS 中文速查与管理端切页性能分析文档
This commit is contained in:
247
docs/admin-page-switch-performance.md
Normal file
247
docs/admin-page-switch-performance.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# 管理端页面切换性能分析与优化任务
|
||||
|
||||
> 分析日期:2026-06-18
|
||||
> 范围:`apps/admin` 侧边栏切换路由时的响应速度(非玩家端)。
|
||||
|
||||
---
|
||||
|
||||
## 任务清单
|
||||
|
||||
- [ ] **measure-baseline**:用 DevTools Network/Performance 记录慢路径基线(`/users`、`/bets`、充值 tab 切换)
|
||||
- [ ] **keepalive-layout**:`ManageLayout` 的 `RouterView` 增加 `KeepAlive` + 列表页 `defineOptions({ name })`
|
||||
- [ ] **list-stale-cache**:高频列表页改 `onActivated` + 模块级/短 TTL 缓存,避免 remount 全量 refetch
|
||||
- [ ] **fix-deposit-tabs**:`DepositManage` 的 `v-if` 改 `v-show` 或 keep-alive 子 tab
|
||||
- [ ] **lighten-agent-manager**:`AgentManager` 挂载 API 合并或按 tab 延迟加载
|
||||
- [ ] **guard-session**:`beforeEach` 去阻塞式 `ensureStaffSession`;`api` 拦截器减少 per-request reconcile
|
||||
- [ ] **bundle-i18n-ep**:Element Plus 按需引入 + 落地 `split-i18n` + `App.vue` CSS 瘦身
|
||||
|
||||
---
|
||||
|
||||
## 现象定义
|
||||
|
||||
用户感知的「切换慢」通常包含三段延迟叠加:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant RouterGuard
|
||||
participant ChunkLoader
|
||||
participant PageView
|
||||
participant API
|
||||
|
||||
User->>RouterGuard: 点击侧边栏
|
||||
RouterGuard->>RouterGuard: ensureStaffSession (可能 HTTP)
|
||||
RouterGuard->>ChunkLoader: 动态 import 页面 chunk
|
||||
ChunkLoader->>PageView: mount 组件
|
||||
PageView->>API: onMounted 并发拉列表
|
||||
API-->>PageView: 渲染表格
|
||||
PageView-->>User: 页面可交互
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 一、架构结论
|
||||
|
||||
| 层级 | 现状 | 对切页的影响 |
|
||||
|------|------|-------------|
|
||||
| 路由 | 全部 lazy load(`apps/admin/src/router/index.ts`) | 首次进入某页需下载 chunk |
|
||||
| 布局 | `ManageLayout.vue` 常驻 | 壳层不重载,合理 |
|
||||
| **页面缓存** | **全项目无 `<KeepAlive>`** | **切走即销毁,回来必 remount + 重拉数据** |
|
||||
| 数据层 | 无 Pinia;仅少数 composable 有模块级缓存 | 绝大多数列表页无跨访问缓存 |
|
||||
| 首屏 bundle | Element Plus 全量 + 三语 i18n 打进主包 | 影响首访/冷启动,对已登录切页影响次之 |
|
||||
|
||||
**最大根因:没有 KeepAlive + 页面 `onMounted` 全量 refetch。**
|
||||
|
||||
---
|
||||
|
||||
## 二、切页时实际发生什么
|
||||
|
||||
### 2.1 路由守卫(每条鉴权路由)
|
||||
|
||||
`apps/admin/src/router/index.ts` 的 `beforeEach`:
|
||||
|
||||
- 有 token 时 **`await ensureStaffSession()`**
|
||||
- `apps/admin/src/utils/session-hydrate.ts` 有 60s TTL,过期后会 **`GET /manage/auth/me`**,**阻塞导航完成**
|
||||
- 访问 smoke-tests 路由时额外 `await ensureLoaded()`
|
||||
|
||||
### 2.2 页面生命周期(无缓存)
|
||||
|
||||
`ManageLayout.vue` 内为裸 `<RouterView />`:
|
||||
|
||||
- 旧页面 **unmount**
|
||||
- 新页面 chunk **动态 import** → **mount**
|
||||
- 典型模式:`onMounted(load)` / 顶层 `void load()`
|
||||
|
||||
仅 **Dashboard 子页** 体验较好:`useAdminDashboard.ts` 模块级 `stats` 缓存,同 session 内切 `/` ↔ `/dashboard/players` 可跳过 API(但 `HomeEntry` 仍可能闪 boot 屏)。
|
||||
|
||||
### 2.3 布局层常驻副作用
|
||||
|
||||
`ManageLayout.vue` `onMounted`:
|
||||
|
||||
- `useDepositPendingCount`:立即请求 + **每 30s 轮询** `pending-count`
|
||||
- `useSmokeTestsAllowed`:一次性权限探测
|
||||
|
||||
不直接阻塞切页,但增加后台并发请求。
|
||||
|
||||
### 2.4 每个 API 请求的同步开销
|
||||
|
||||
`apps/admin/src/api.ts` 请求拦截器对每个请求调用 `reconcileStaffSessionFromToken()`(JWT decode + localStorage)。列表页 mount 时常 **并发 3–10 个请求**,同步开销被放大。
|
||||
|
||||
---
|
||||
|
||||
## 三、按影响排序的瓶颈清单
|
||||
|
||||
### P0 — 切换体验(每次切页都痛)
|
||||
|
||||
**1. 无 KeepAlive,列表页反复 remount + refetch**
|
||||
|
||||
受影响页面(模式相同):
|
||||
|
||||
- `Bets.vue`、`Cashback.vue`
|
||||
- `Matches.vue`、`MatchesOutrights.vue`
|
||||
- `DepositOrders.vue`、`StaffManage.vue`
|
||||
- `Contents.vue`、`FinanceLogs.vue` 等
|
||||
|
||||
**2. 重型页面挂载 API burst**
|
||||
|
||||
`AgentManager.vue`(`/users`)— 约 2900 行 SFC,`onMounted` 并行:
|
||||
|
||||
- `GET /admin/users/page-init`
|
||||
- `GET /admin/users`(全量玩家)
|
||||
- `GET /admin/agents?level=1`
|
||||
- page-init 后再按层级 **N+1** 拉子代理
|
||||
|
||||
每次从其他页回到 `/users` 都会重复上述 burst。
|
||||
|
||||
**3. Tab 用 `v-if` 导致子页销毁**
|
||||
|
||||
`DepositManage.vue`:
|
||||
|
||||
```vue
|
||||
<DepositOrders v-if="activeTab === 'orders'" />
|
||||
<PaymentMethods v-if="activeTab === 'methods'" />
|
||||
```
|
||||
|
||||
同页内切换 tab 也会 destroy + `onMounted(fetchList)`。
|
||||
|
||||
**4. Matches 展开面板扇出请求**
|
||||
|
||||
`LeagueMatchesPanel.vue` `watch(..., { immediate: true })`:恢复 session 展开最多 3 个联赛时,**最多 3 路** `GET /admin/matches`;折叠再展开会 remount 重拉。
|
||||
|
||||
### P1 — 间歇性卡顿(特定路径 / 时间)
|
||||
|
||||
**5. Session hydrate 阻塞导航**
|
||||
|
||||
60s TTL 过期后,**每次切页**先等 `/manage/auth/me`。弱网或后端慢时,侧边栏点击后「卡住」数秒。
|
||||
|
||||
**6. HomeEntry 回 Dashboard 闪屏**
|
||||
|
||||
`HomeEntry.vue`:`onBeforeMount` 再次 `ensureStaffSession` + `booting` 全屏 loading(router 已做过 hydrate)。
|
||||
|
||||
**7. 首次进入大 chunk 的 JS 解析**
|
||||
|
||||
| 页面 | 风险 |
|
||||
|------|------|
|
||||
| `AgentManager.vue` | 巨型 SFC + 多子组件 |
|
||||
| `Settlement.vue` | ~1500 行 + async echarts |
|
||||
| `Contents.vue` | `ContentRichEditor.vue` |
|
||||
|
||||
### P2 — 首屏 / 冷启动
|
||||
|
||||
**8. Element Plus 全量注册**
|
||||
|
||||
`main.ts` 全量 `app.use(ElementPlus)` + `element-plus/dist/index.css`,无按需引入。
|
||||
|
||||
**9. i18n 三语未真正拆包**
|
||||
|
||||
`admin-messages.ts` 仍静态 import zh/en/ms 全文;`split-i18n.mjs` 未落地,首屏携带全部语言文案。
|
||||
|
||||
**10. App.vue 全局 CSS ~1900 行**
|
||||
|
||||
无 scoped 的暗色/浅色双套 Element 覆盖,与全量 EP CSS 叠加。
|
||||
|
||||
---
|
||||
|
||||
## 四、问题分层矩阵
|
||||
|
||||
| 症状 | 最可能原因 | 验证方式 |
|
||||
|------|-----------|----------|
|
||||
| 任意页切回上一页都慢 | 无 KeepAlive + onMounted refetch | Network:切回同页重复相同 API |
|
||||
| 仅 `/users` 特别慢 | AgentManager 多路并行 API + 大 chunk | mount 时 3+ 请求;Performance 看 JS |
|
||||
| 偶尔点菜单无反应数秒 | `ensureStaffSession` 阻塞 | 切页瞬间是否有 `/manage/auth/me` |
|
||||
| 充值页 tab 切换慢 | DepositManage `v-if` | 切 tab 是否重复 `deposit-orders` 请求 |
|
||||
| 首次进某页慢、之后再进仍慢 | 大 chunk + 仍无缓存 | 对比首次/二次 Network |
|
||||
| 整体首次打开就慢 | EP 全量 + i18n 三语 + 全局 CSS | `pnpm --filter @thebet365/admin build:analyze` |
|
||||
|
||||
---
|
||||
|
||||
## 五、优化路径(分阶段)
|
||||
|
||||
### 阶段 A — 切页体验(1–2 天,收益最大)
|
||||
|
||||
1. `ManageLayout.vue` 的 `<RouterView>` 外包 `<KeepAlive :max="8">`,列表页 `defineOptions({ name })`
|
||||
2. 列表页改 `onActivated` + stale-while-revalidate(有缓存先展示,后台静默刷新)
|
||||
3. `DepositManage.vue`:`v-if` → `v-show` 或 keep-alive 两个 tab
|
||||
4. `HomeEntry.vue`:去掉重复 hydrate / 仅首次 boot
|
||||
|
||||
### 阶段 B — 重型页与 API(2–3 天)
|
||||
|
||||
1. 拆分 `AgentManager.vue`:按 tab lazy,或合并 bootstrap 为单一 `page-init` 接口
|
||||
2. 提取通用 `useListCache(key, fetcher, ttl)` 给 Bets/Users/Deposit 等
|
||||
3. `LeagueMatchesPanel`:对已加载 `leagueId` 短 TTL 缓存
|
||||
4. `api.ts`:`reconcileStaffSessionFromToken` 移到 token 变更时,而非每请求
|
||||
|
||||
### 阶段 C — 守卫与 bundle(3–5 天)
|
||||
|
||||
1. `beforeEach` 改为同步 JWT/localStorage 校验;`/me` 仅登录/刷新时调用
|
||||
2. Element Plus 改按需 + 去全量 CSS
|
||||
3. 执行/合入 i18n `split-i18n.mjs`,首屏只加载当前语言
|
||||
4. 瘦身 `App.vue` 全局样式
|
||||
|
||||
### 阶段 D — 度量与验收
|
||||
|
||||
```bash
|
||||
pnpm --filter @thebet365/admin build:analyze
|
||||
```
|
||||
|
||||
验收指标(Chrome DevTools,Fast 3G):
|
||||
|
||||
- 切回已访问列表页:无重复全量列表 API(或仅 background refresh)
|
||||
- `/users` 二次进入:API 数从 3+ 降到 0–1
|
||||
- 侧边栏切换:guard 阶段无阻塞性 `/me`(60s 内)
|
||||
|
||||
---
|
||||
|
||||
## 六、当前不必优先动的部分
|
||||
|
||||
- **ECharts**:已 async chunk,仅 dashboard/settlement 加载
|
||||
- **ContentRichEditor**:未全局引入,仅 contents 路由
|
||||
- **deposit 30s 轮询**:后台流量,通常不是切页主因
|
||||
- **ManageLayout computed 菜单**:开销相对小
|
||||
|
||||
---
|
||||
|
||||
## 七、推荐落地顺序
|
||||
|
||||
1. **只做一处**:KeepAlive + 列表页 activated 缓存(阶段 A)
|
||||
2. **`/users` 最慢**:在 A 之后做 AgentManager API 合并/延迟加载(阶段 B)
|
||||
3. **首屏整体慢**:再动 Element Plus / i18n 拆分(阶段 C)
|
||||
|
||||
---
|
||||
|
||||
## 八、关键文件索引
|
||||
|
||||
| 职责 | 路径 |
|
||||
|------|------|
|
||||
| 路由 + beforeEach | `apps/admin/src/router/index.ts` |
|
||||
| 布局壳 | `apps/admin/src/layouts/ManageLayout.vue` |
|
||||
| Session 水合 / TTL | `apps/admin/src/utils/session-hydrate.ts` |
|
||||
| Auth store | `apps/admin/src/stores/auth.ts` |
|
||||
| Axios 拦截器 | `apps/admin/src/api.ts` |
|
||||
| Dashboard 入口 | `apps/admin/src/views/HomeEntry.vue` |
|
||||
| Dashboard 数据缓存 | `apps/admin/src/composables/useAdminDashboard.ts` |
|
||||
| Deposit 轮询 | `apps/admin/src/composables/useDepositPendingCount.ts` |
|
||||
| Bootstrap | `apps/admin/src/main.ts` |
|
||||
| Vite 分包 | `apps/admin/vite.config.ts` |
|
||||
| 重型用户页 | `apps/admin/src/views/AgentManager.vue` |
|
||||
| 充值 tab | `apps/admin/src/views/DepositManage.vue` |
|
||||
Reference in New Issue
Block a user