- 阶段 A: 增加 KeepAlive 缓存高频列表页,改用 onActivated 进行后台静默刷新,优化 tab 切换与 HomeEntry 闪屏 - 阶段 B: beforeEach 守卫在 TTL 内走同步快速路径,api 请求拦截器缓存 token 避免重复解析 JWT - 阶段 C: 引入 unplugin 插件启用 Element Plus 组件按需加载,清理 App.vue 中 960+ 行冗余暗色主题 CSS - i18n 拆包: 将三语文案包提取为独立懒加载 chunks,主包体积从 209KB 减少至 99KB (降低 53%)
9.1 KiB
管理端页面切换性能分析与优化任务
分析日期:2026-06-18
范围:apps/admin侧边栏切换路由时的响应速度(非玩家端)。
任务清单
- measure-baseline:用 DevTools Network/Performance 记录慢路径基线(
/users、/bets、充值 tab 切换) - keepalive-layout:
ManageLayout的RouterView增加KeepAlive+ 列表页defineOptions({ name })(2026-06-18) - list-stale-cache:高频列表页改
onActivated+ stale-while-revalidate,避免 remount 全量 refetch(2026-06-18) - fix-deposit-tabs:
DepositManage的v-if改v-show(2026-06-18) - lighten-agent-manager:
AgentManager挂载 API 合并或按 tab 延迟加载(onActivated 部分已做,拆分 SFC 待后续) - guard-session:
beforeEach去阻塞式ensureStaffSession(TTL 内同步快速路径);api拦截器减少 per-request reconcile(2026-06-18) - bundle-i18n-ep:Element Plus 按需引入 + 落地
split-i18n+App.vueCSS 瘦身(2026-06-18)
现象定义
用户感知的「切换慢」通常包含三段延迟叠加:
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-countuseSmokeTestsAllowed:一次性权限探测
不直接阻塞切页,但增加后台并发请求。
2.4 每个 API 请求的同步开销
apps/admin/src/api.ts 请求拦截器对每个请求调用 reconcileStaffSessionFromToken()(JWT decode + localStorage)。列表页 mount 时常 并发 3–10 个请求,同步开销被放大。
三、按影响排序的瓶颈清单
P0 — 切换体验(每次切页都痛)
1. 无 KeepAlive,列表页反复 remount + refetch
受影响页面(模式相同):
Bets.vue、Cashback.vueMatches.vue、MatchesOutrights.vueDepositOrders.vue、StaffManage.vueContents.vue、FinanceLogs.vue等
2. 重型页面挂载 API burst
AgentManager.vue(/users)— 约 2900 行 SFC,onMounted 并行:
GET /admin/users/page-initGET /admin/users(全量玩家)GET /admin/agents?level=1- page-init 后再按层级 N+1 拉子代理
每次从其他页回到 /users 都会重复上述 burst。
3. Tab 用 v-if 导致子页销毁
DepositManage.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 天,收益最大)
ManageLayout.vue的<RouterView>外包<KeepAlive :max="8">,列表页defineOptions({ name })- 列表页改
onActivated+ stale-while-revalidate(有缓存先展示,后台静默刷新) DepositManage.vue:v-if→v-show或 keep-alive 两个 tabHomeEntry.vue:去掉重复 hydrate / 仅首次 boot
阶段 B — 重型页与 API(2–3 天)
- 拆分
AgentManager.vue:按 tab lazy,或合并 bootstrap 为单一page-init接口 - 提取通用
useListCache(key, fetcher, ttl)给 Bets/Users/Deposit 等 LeagueMatchesPanel:对已加载leagueId短 TTL 缓存api.ts:reconcileStaffSessionFromToken移到 token 变更时,而非每请求
阶段 C — 守卫与 bundle(3–5 天)
beforeEach改为同步 JWT/localStorage 校验;/me仅登录/刷新时调用- Element Plus 改按需 + 去全量 CSS
- 执行/合入 i18n
split-i18n.mjs,首屏只加载当前语言 - 瘦身
App.vue全局样式
阶段 D — 度量与验收
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 菜单:开销相对小
七、推荐落地顺序
- 只做一处:KeepAlive + 列表页 activated 缓存(阶段 A)
/users最慢:在 A 之后做 AgentManager API 合并/延迟加载(阶段 B)- 首屏整体慢:再动 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 |