Files
EasyFlow/easyflow-ui-admin/packages/effects/layouts/src/basic/menu/use-navigation.ts
陈子默 306b08d55a perf: 懒加载表现优化
- 优化路由首进页面过渡策略,减少遮罩阻塞感

- 为菜单导航增加组件预取,降低首次点击等待

- 缩短页面 loading 遮罩过渡时长
2026-02-24 16:45:14 +08:00

123 lines
3.1 KiB
TypeScript

import type { RouteRecordNormalized } from 'vue-router';
import { useRouter } from 'vue-router';
import { isHttpUrl, openRouteInNewWindow, openWindow } from '@easyflow/utils';
function useNavigation() {
const router = useRouter();
const routeMetaMap = new Map<string, RouteRecordNormalized>();
const prefetchedPaths = new Set<string>();
const prefetchingPaths = new Set<string>();
// 初始化路由映射
const initRouteMetaMap = () => {
const routes = router.getRoutes();
routes.forEach((route) => {
routeMetaMap.set(route.path, route);
});
};
initRouteMetaMap();
// 监听路由变化
router.afterEach(() => {
initRouteMetaMap();
});
// 检查是否应该在新窗口打开
const shouldOpenInNewWindow = (path: string): boolean => {
if (isHttpUrl(path)) {
return true;
}
const route = routeMetaMap.get(path);
// 如果有外链或者设置了在新窗口打开,返回 true
return !!(route?.meta?.link || route?.meta?.openInNewWindow);
};
const resolveHref = (path: string): string => {
return router.resolve(path).href;
};
const prefetch = (path: string) => {
if (
isHttpUrl(path) ||
prefetchedPaths.has(path) ||
prefetchingPaths.has(path)
) {
return;
}
const { matched } = router.resolve(path);
if (matched.length === 0) {
return;
}
const componentLoadTasks: Array<Promise<unknown>> = [];
matched.forEach((route) => {
const component = route.components?.default;
if (typeof component !== 'function' || component.length > 0) {
return;
}
try {
const loaded = (component as () => unknown)();
if (loaded && typeof (loaded as Promise<unknown>).then === 'function') {
componentLoadTasks.push(loaded as Promise<unknown>);
}
} catch {
// 预取失败不影响正常导航
}
});
if (componentLoadTasks.length === 0) {
prefetchedPaths.add(path);
return;
}
prefetchingPaths.add(path);
void Promise.allSettled(componentLoadTasks)
.then(() => {
prefetchedPaths.add(path);
})
.finally(() => {
prefetchingPaths.delete(path);
});
};
const navigation = async (path: string) => {
try {
const route = routeMetaMap.get(path);
const { openInNewWindow = false, query = {}, link } = route?.meta ?? {};
// 检查是否有外链
if (link && typeof link === 'string') {
openWindow(link, { target: '_blank' });
return;
}
if (isHttpUrl(path)) {
openWindow(path, { target: '_blank' });
} else if (openInNewWindow) {
openRouteInNewWindow(resolveHref(path));
} else {
prefetch(path);
await router.push({
path,
query,
});
}
} catch (error) {
console.error('Navigation failed:', error);
throw error;
}
};
const willOpenedByWindow = (path: string) => {
return shouldOpenInNewWindow(path);
};
return { navigation, prefetch, willOpenedByWindow };
}
export { useNavigation };