perf: prefetch ai pages and soften route loading

This commit is contained in:
2026-06-14 08:27:51 +08:00
parent 829d85625f
commit 58ce877e64
3 changed files with 93 additions and 12 deletions
+2
View File
@@ -13,6 +13,7 @@ import { normalizeHomePath } from '#/utils/legacy-home';
import { generateAccess } from './access'; import { generateAccess } from './access';
import { isLightTabRoute } from './light-menu'; import { isLightTabRoute } from './light-menu';
import { prefetchAiPlatformPages } from './prefetch';
function filterLightTabHistory<T extends { tabs?: unknown[] }>(history: T) { function filterLightTabHistory<T extends { tabs?: unknown[] }>(history: T) {
const tabs = Array.isArray(history.tabs) ? history.tabs : []; const tabs = Array.isArray(history.tabs) ? history.tabs : [];
@@ -197,6 +198,7 @@ function setupAccessGuard(router: Router) {
accessStore.setAccessMenus(accessibleMenus); accessStore.setAccessMenus(accessibleMenus);
accessStore.setAccessRoutes(accessibleRoutes); accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true); accessStore.setIsAccessChecked(true);
prefetchAiPlatformPages();
// 子应用根路径重定向(/app/hr -> /app/hr/xxx // 子应用根路径重定向(/app/hr -> /app/hr/xxx
if (appContextStore.isSubApp) { if (appContextStore.isSubApp) {
+47
View File
@@ -0,0 +1,47 @@
type IdleCallbackHandle = number;
type IdleCallbackOptions = {
timeout?: number;
};
type WindowWithIdleCallback = Window & {
requestIdleCallback?: (
callback: () => void,
options?: IdleCallbackOptions,
) => IdleCallbackHandle;
};
let prefetched = false;
const prefetchTasks = [
() => import('#/views/ai-platform/agent/index.vue'),
() => import('#/views/_core/agent-chat/index.vue'),
() => import('#/views/ai-platform/workflow/index.vue'),
() => import('#/views/ai-platform/workflow-runs/index.vue'),
() => import('#/views/ai-platform/model/index.vue'),
() => import('#/views/ai-platform/knowledge/index.vue'),
];
function scheduleIdle(callback: () => void) {
const browserWindow = window as WindowWithIdleCallback;
if (browserWindow.requestIdleCallback) {
browserWindow.requestIdleCallback(callback, { timeout: 3000 });
return;
}
window.setTimeout(callback, 1200);
}
function prefetchAiPlatformPages() {
if (prefetched || typeof window === 'undefined') {
return;
}
prefetched = true;
scheduleIdle(() => {
void Promise.allSettled(prefetchTasks.map((task) => task()));
});
}
export { prefetchAiPlatformPages };
@@ -5,41 +5,73 @@ import { preferences } from '@vben/preferences';
function useContentSpinner() { function useContentSpinner() {
const spinning = ref(false); const spinning = ref(false);
const startTime = ref(0); const visibleStartTime = ref(0);
const router = useRouter(); const router = useRouter();
const minShowTime = 500; // 最小显示时间 const showDelay = 220;
const minVisibleTime = 160;
const enableLoading = computed(() => preferences.transition.loading); const enableLoading = computed(() => preferences.transition.loading);
let pending = false;
let showTimer: ReturnType<typeof setTimeout> | undefined;
let hideTimer: ReturnType<typeof setTimeout> | undefined;
// 结束加载动画 const clearTimers = () => {
const onEnd = () => { clearTimeout(showTimer);
clearTimeout(hideTimer);
showTimer = undefined;
hideTimer = undefined;
};
const onStart = () => {
if (!enableLoading.value) { if (!enableLoading.value) {
return; return;
} }
const processTime = performance.now() - startTime.value;
if (processTime < minShowTime) { pending = true;
setTimeout(() => { clearTimers();
showTimer = setTimeout(() => {
if (!pending) {
return;
}
visibleStartTime.value = performance.now();
spinning.value = true;
}, showDelay);
};
const onEnd = () => {
pending = false;
clearTimeout(showTimer);
showTimer = undefined;
if (!enableLoading.value || !spinning.value) {
return;
}
const visibleTime = performance.now() - visibleStartTime.value;
if (visibleTime < minVisibleTime) {
hideTimer = setTimeout(() => {
spinning.value = false; spinning.value = false;
}, minShowTime - processTime); hideTimer = undefined;
}, minVisibleTime - visibleTime);
} else { } else {
spinning.value = false; spinning.value = false;
} }
}; };
// 路由前置守卫
router.beforeEach((to) => { router.beforeEach((to) => {
if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) {
return true; return true;
} }
startTime.value = performance.now();
spinning.value = true; onStart();
return true; return true;
}); });
// 路由后置守卫
router.afterEach((to) => { router.afterEach((to) => {
if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) {
return true; return true;
} }
onEnd(); onEnd();
return true; return true;
}); });