import { ComponentLoader } from './ComponentLoader.js'
|
import { RouteValidator } from './RouteValidator.js'
|
import { RouteTransformer } from './RouteTransformer.js'
|
const DEFAULT_WARMUP_LIMIT = 12
|
const HOME_COMPONENT_PATH = '/dashboard/console'
|
class RouteRegistry {
|
constructor(router, options = {}) {
|
this.router = router
|
this.componentLoader = options.componentLoader || new ComponentLoader()
|
this.validator = new RouteValidator()
|
this.transformer = new RouteTransformer(this.componentLoader)
|
this.removeRouteFns = []
|
this.registered = false
|
}
|
/**
|
* 注册动态路由
|
*/
|
register(menuList) {
|
if (this.registered) {
|
console.warn('[RouteRegistry] 路由已注册,跳过重复注册')
|
return
|
}
|
const validationResult = this.validator.validate(menuList)
|
if (!validationResult.valid) {
|
throw new Error(`路由配置验证失败: ${validationResult.errors.join(', ')}`)
|
}
|
const removeRouteFns = []
|
const existingRoutePaths = new Set(
|
(this.router.getRoutes?.() || []).map((route) => route?.path).filter(Boolean)
|
)
|
menuList.forEach((route) => {
|
if (route?.meta?.staticRoute) {
|
return
|
}
|
|
const routeConfig = this.transformer.transform(route)
|
const routePath = routeConfig?.path
|
|
if (routePath && existingRoutePaths.has(routePath)) {
|
console.warn(`[RouteRegistry] 检测到重复路径,已跳过动态注册: ${routePath}`)
|
return
|
}
|
|
if (route.name && this.router.hasRoute(route.name)) {
|
console.warn(`[RouteRegistry] 检测到重复名称,已跳过动态注册: ${route.name}`)
|
return
|
}
|
|
const removeRouteFn = this.router.addRoute(routeConfig)
|
removeRouteFns.push(removeRouteFn)
|
if (routePath) {
|
existingRoutePaths.add(routePath)
|
}
|
})
|
this.removeRouteFns = removeRouteFns
|
this.registered = true
|
}
|
/**
|
* 移除所有动态路由
|
*/
|
unregister() {
|
this.removeRouteFns.forEach((fn) => fn())
|
this.removeRouteFns = []
|
this.registered = false
|
}
|
/**
|
* 检查是否已注册
|
*/
|
isRegistered() {
|
return this.registered
|
}
|
/**
|
* 获取移除函数列表(用于 store 管理)
|
*/
|
getRemoveRouteFns() {
|
return this.removeRouteFns
|
}
|
/**
|
* 标记为已注册(用于错误处理场景,避免重复请求)
|
*/
|
markAsRegistered() {
|
this.registered = true
|
}
|
/**
|
* 空闲时预热高频页面,降低登录后首次切页的冷加载成本。
|
*/
|
warm(menuList, options = {}) {
|
const limit = Number.isFinite(options.limit) ? options.limit : DEFAULT_WARMUP_LIMIT
|
const paths = collectWarmupPaths(menuList, limit)
|
if (paths.length === 0) {
|
return
|
}
|
const schedule = globalThis.requestIdleCallback
|
? (task) => globalThis.requestIdleCallback(task, { timeout: 1200 })
|
: (task) => setTimeout(task, 80)
|
schedule(() => {
|
void warmSequentially(paths, this.componentLoader)
|
})
|
}
|
}
|
function collectWarmupPaths(menuList, limit) {
|
const paths = []
|
const visited = new Set()
|
const walk = (items) => {
|
if (!Array.isArray(items)) {
|
return
|
}
|
for (const item of items) {
|
if (paths.length >= limit) {
|
return
|
}
|
const componentPath = typeof item?.component === 'string' ? item.component.trim() : ''
|
if (
|
componentPath &&
|
componentPath !== '/' &&
|
componentPath !== HOME_COMPONENT_PATH &&
|
!visited.has(componentPath) &&
|
!item?.meta?.isFirstLevel
|
) {
|
visited.add(componentPath)
|
paths.push(componentPath)
|
}
|
if (Array.isArray(item?.children) && item.children.length > 0) {
|
walk(item.children)
|
}
|
}
|
}
|
walk(menuList)
|
return paths
|
}
|
async function warmSequentially(paths, componentLoader) {
|
for (const componentPath of paths) {
|
await componentLoader.warm(componentPath)
|
}
|
}
|
export { RouteRegistry }
|