From 6a488fe4526664214ddfc8839835d1942abdf44f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 12:01:01 +0800 Subject: [PATCH] =?UTF-8?q?perf(layout):=20=E5=88=86=E5=8C=85=E9=A2=84?= =?UTF-8?q?=E7=83=AD=E6=94=B9=E4=B8=BA=E8=8F=9C=E5=8D=95=E6=82=AC=E5=81=9C?= =?UTF-8?q?=E6=87=92=E9=A2=84=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - prefetchRoutes 由「登录后全量预热」改为 WarmRouteByPath 按需预热(幂等、弱网跳过) - 侧边栏菜单项 mouseenter/focusin 触发预热;分组菜单预热其全部子页面 - 首屏零额外请求,仅预热用户已表达意图的页面 --- src/layouts/DefaultLayout.vue | 40 ++++++++--- src/utils/prefetchRoutes.ts | 124 +++++++++++----------------------- 2 files changed, 69 insertions(+), 95 deletions(-) diff --git a/src/layouts/DefaultLayout.vue b/src/layouts/DefaultLayout.vue index c47d270..04838c4 100644 --- a/src/layouts/DefaultLayout.vue +++ b/src/layouts/DefaultLayout.vue @@ -22,7 +22,8 @@ import RouteProgress from '@/components/RouteProgress.vue' import PageMask from '@/components/PageMask.vue' import RoleSwitcher from '@/components/RoleSwitcher.vue' import { staticRoutes } from '@/router' -import { prefetchVisibleRoutes } from '@/utils/prefetchRoutes' +import { WarmRouteByPath } from '@/utils/prefetchRoutes' +import type { MenuItem } from '@/types/api' const appStore = useAppStore() const userStore = useUserStore() @@ -109,15 +110,21 @@ onMounted(() => { unreadTimer = setInterval(() => { void loadUnreadCount() }, 60000) - // 分包预热:把当前用户可访问页面的懒加载 chunk 提前拉取并缓存(详见 utils/prefetchRoutes) - prefetchVisibleRoutes(staticRoutes, (p) => userStore.hasMenuPath(p)) }) -// 菜单变化(切换角色/后端菜单刷新)后重新预热;工具内部按「可见路径集合」签名去重 -watch( - () => userStore.menus, - () => prefetchVisibleRoutes(staticRoutes, (p) => userStore.hasMenuPath(p)), -) +// === 菜单意图预热(懒预取) === +// 鼠标悬停 / 键盘聚焦菜单项时,提前拉取该页面的懒加载分包(幂等,详见 utils/prefetchRoutes), +// 用户真正点击时命中 72h 强缓存直接打开。 + +/** 单个菜单项:预热其页面分包 */ +function WarmMenuIntent(path: string | undefined): void { + WarmRouteByPath(staticRoutes, path) +} + +/** 分组菜单:预热其全部子页面 */ +function WarmGroupIntent(menu: MenuItem): void { + for (const child of menu.Children || []) WarmMenuIntent(child.Path) +} onBeforeUnmount(() => { if (unreadTimer) clearInterval(unreadTimer) @@ -389,7 +396,12 @@ const isFaIcon = (icon: string | undefined | null): boolean => { >