@vlian/framework 1.2.16 → 1.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/analytics.umd.js +1 -1
  2. package/dist/core/router/RouterManager.cjs +32 -2
  3. package/dist/core/router/RouterManager.cjs.map +1 -1
  4. package/dist/core/router/RouterManager.d.ts +5 -0
  5. package/dist/core/router/RouterManager.js +33 -3
  6. package/dist/core/router/RouterManager.js.map +1 -1
  7. package/dist/core/router/adapter/react-router/ReactRouterAdapter.cjs.map +1 -1
  8. package/dist/core/router/adapter/react-router/ReactRouterAdapter.js.map +1 -1
  9. package/dist/core/router/dev/RouterDevTools.cjs.map +1 -1
  10. package/dist/core/router/dev/RouterDevTools.js.map +1 -1
  11. package/dist/core/router/middleware/RouterMiddlewareManager.cjs +24 -4
  12. package/dist/core/router/middleware/RouterMiddlewareManager.cjs.map +1 -1
  13. package/dist/core/router/middleware/RouterMiddlewareManager.d.ts +1 -0
  14. package/dist/core/router/middleware/RouterMiddlewareManager.js +24 -4
  15. package/dist/core/router/middleware/RouterMiddlewareManager.js.map +1 -1
  16. package/dist/core/router/middleware/types.cjs.map +1 -1
  17. package/dist/core/router/middleware/types.d.ts +1 -1
  18. package/dist/core/router/middleware/types.js.map +1 -1
  19. package/dist/core/router/navigation/RouterNavigation.cjs +69 -14
  20. package/dist/core/router/navigation/RouterNavigation.cjs.map +1 -1
  21. package/dist/core/router/navigation/RouterNavigation.d.ts +3 -0
  22. package/dist/core/router/navigation/RouterNavigation.js +69 -14
  23. package/dist/core/router/navigation/RouterNavigation.js.map +1 -1
  24. package/dist/core/router/performance/RouteCache.cjs +34 -13
  25. package/dist/core/router/performance/RouteCache.cjs.map +1 -1
  26. package/dist/core/router/performance/RouteCache.d.ts +8 -2
  27. package/dist/core/router/performance/RouteCache.js +34 -13
  28. package/dist/core/router/performance/RouteCache.js.map +1 -1
  29. package/dist/core/router/performance/RoutePreloader.cjs +89 -22
  30. package/dist/core/router/performance/RoutePreloader.cjs.map +1 -1
  31. package/dist/core/router/performance/RoutePreloader.d.ts +9 -1
  32. package/dist/core/router/performance/RoutePreloader.js +89 -22
  33. package/dist/core/router/performance/RoutePreloader.js.map +1 -1
  34. package/dist/core/router/types.d.ts +22 -5
  35. package/dist/core/router/types.js.map +1 -1
  36. package/dist/core/router/utils/adapters/react-router/transform.cjs +37 -64
  37. package/dist/core/router/utils/adapters/react-router/transform.cjs.map +1 -1
  38. package/dist/core/router/utils/adapters/react-router/transform.js +37 -59
  39. package/dist/core/router/utils/adapters/react-router/transform.js.map +1 -1
  40. package/dist/core/router/utils/transform.cjs +79 -70
  41. package/dist/core/router/utils/transform.cjs.map +1 -1
  42. package/dist/core/router/utils/transform.d.ts +1 -1
  43. package/dist/core/router/utils/transform.js +80 -71
  44. package/dist/core/router/utils/transform.js.map +1 -1
  45. package/dist/core/router/validation/RouterConfigValidator.d.ts +66 -4
  46. package/dist/core/router/validation/schema.cjs +71 -2
  47. package/dist/core/router/validation/schema.cjs.map +1 -1
  48. package/dist/core/router/validation/schema.d.ts +102 -6
  49. package/dist/core/router/validation/schema.js +71 -2
  50. package/dist/core/router/validation/schema.js.map +1 -1
  51. package/dist/index.umd.js +677 -428
  52. package/dist/index.umd.js.map +1 -1
  53. package/dist/request.umd.js +1 -1
  54. package/dist/state.umd.js +1 -1
  55. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/router/performance/RoutePreloader.ts"],"sourcesContent":["/**\n * 路由预加载管理器\n * 提供路由组件的预加载功能\n */\n\nimport { logger } from '../../../utils';\nimport type { RouteConfig } from '../types';\nimport type { ComponentImport } from '../types';\n\n/**\n * 预加载策略\n */\nexport enum PreloadStrategy {\n /**\n * 不预加载\n */\n NONE = 'none',\n\n /**\n * 预加载当前路由的下一级路由\n */\n NEXT_LEVEL = 'next-level',\n\n /**\n * 预加载所有路由\n */\n ALL = 'all',\n\n /**\n * 预加载可见路由(根据路由配置的优先级)\n */\n VISIBLE = 'visible',\n}\n\n/**\n * 预加载配置\n */\nexport interface PreloadConfig {\n /**\n * 预加载策略\n * @default PreloadStrategy.NEXT_LEVEL\n */\n strategy?: PreloadStrategy;\n\n /**\n * 预加载延迟(毫秒)\n * @default 1000\n */\n delay?: number;\n\n /**\n * 预加载优先级阈值(只预加载优先级小于等于此值的路由)\n * @default 10\n */\n priorityThreshold?: number;\n\n /**\n * 预加载超时时间(毫秒)\n * @default 5000\n */\n timeout?: number;\n}\n\n/**\n * 预加载任务\n */\ninterface PreloadTask {\n /**\n * 组件导入函数\n */\n importFn: ComponentImport;\n /**\n * 路由名称\n */\n routeName: string;\n /**\n * 优先级\n */\n priority: number;\n /**\n * 状态\n */\n status: 'pending' | 'loading' | 'loaded' | 'failed';\n /**\n * 开始时间\n */\n startTime?: number;\n}\n\n/**\n * 路由预加载管理器\n */\nexport class RoutePreloader {\n private tasks: Map<string, PreloadTask> = new Map();\n private config: Required<PreloadConfig>;\n private preloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(config: PreloadConfig = {}) {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NEXT_LEVEL,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n /**\n * 注册路由用于预加载\n */\n registerRoute(route: RouteConfig): void {\n if (!route.page && !route.layout) {\n return;\n }\n\n const priority = route.handle?.order ?? 999;\n\n // 根据策略决定是否注册\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n if (\n this.config.strategy === PreloadStrategy.VISIBLE &&\n priority > this.config.priorityThreshold\n ) {\n return;\n }\n\n // 注册页面组件\n if (route.page && typeof route.page === 'function') {\n this.tasks.set(`${route.name}_page`, {\n importFn: route.page,\n routeName: route.name,\n priority,\n status: 'pending',\n });\n }\n\n // 注册布局组件\n if (route.layout && typeof route.layout === 'function') {\n this.tasks.set(`${route.name}_layout`, {\n importFn: route.layout,\n routeName: route.name,\n priority,\n status: 'pending',\n });\n }\n\n // 递归注册子路由\n if (route.children) {\n route.children.forEach((child) => this.registerRoute(child));\n }\n }\n\n /**\n * 注册多个路由\n */\n registerRoutes(routes: RouteConfig[]): void {\n routes.forEach((route) => this.registerRoute(route));\n }\n\n /**\n * 预加载指定路由\n */\n async preloadRoute(routeName: string): Promise<void> {\n const tasks = Array.from(this.tasks.values()).filter(\n (task) => task.routeName === routeName && task.status === 'pending'\n );\n\n if (tasks.length === 0) {\n return;\n }\n\n await Promise.all(tasks.map((task) => this.preloadTask(task)));\n }\n\n /**\n * 预加载任务\n */\n private async preloadTask(task: PreloadTask): Promise<void> {\n if (task.status !== 'pending') {\n return;\n }\n\n task.status = 'loading';\n task.startTime = Date.now();\n\n try {\n // 设置超时\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('预加载超时')), this.config.timeout);\n });\n\n // 执行预加载\n await Promise.race([task.importFn(), timeoutPromise]);\n\n task.status = 'loaded';\n const duration = Date.now() - (task.startTime || 0);\n logger.debug(`路由组件预加载成功: ${task.routeName} (${duration}ms)`);\n } catch (error) {\n task.status = 'failed';\n logger.warn(`路由组件预加载失败: ${task.routeName}`, error);\n }\n }\n\n /**\n * 开始预加载(根据策略)\n */\n startPreload(): void {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n // 延迟预加载,避免影响初始渲染\n this.preloadTimer = setTimeout(() => {\n this.executePreload();\n }, this.config.delay);\n }\n\n /**\n * 执行预加载\n */\n private async executePreload(): Promise<void> {\n const tasks = Array.from(this.tasks.values())\n .filter((task) => task.status === 'pending')\n .sort((a, b) => a.priority - b.priority);\n\n if (tasks.length === 0) {\n return;\n }\n\n logger.debug(`开始预加载 ${tasks.length} 个路由组件`);\n\n // 根据策略决定预加载哪些任务\n let tasksToPreload: PreloadTask[] = [];\n\n switch (this.config.strategy) {\n case PreloadStrategy.ALL:\n tasksToPreload = tasks;\n break;\n case PreloadStrategy.NEXT_LEVEL:\n // 只预加载优先级最低的(最可能访问的)\n tasksToPreload = tasks.slice(0, Math.min(5, tasks.length));\n break;\n case PreloadStrategy.VISIBLE:\n tasksToPreload = tasks.filter(\n (task) => task.priority <= this.config.priorityThreshold\n );\n break;\n default:\n return;\n }\n\n // 并行预加载(限制并发数)\n const concurrency = 3;\n for (let i = 0; i < tasksToPreload.length; i += concurrency) {\n const batch = tasksToPreload.slice(i, i + concurrency);\n await Promise.all(batch.map((task) => this.preloadTask(task)));\n }\n\n logger.debug('路由组件预加载完成');\n }\n\n /**\n * 停止预加载\n */\n stopPreload(): void {\n if (this.preloadTimer) {\n clearTimeout(this.preloadTimer);\n this.preloadTimer = null;\n }\n }\n\n /**\n * 清空所有任务\n */\n clear(): void {\n this.tasks.clear();\n this.stopPreload();\n logger.debug('路由预加载任务已清空');\n }\n\n /**\n * 获取预加载统计信息\n */\n getStats(): {\n total: number;\n pending: number;\n loading: number;\n loaded: number;\n failed: number;\n } {\n const stats = {\n total: this.tasks.size,\n pending: 0,\n loading: 0,\n loaded: 0,\n failed: 0,\n };\n\n for (const task of this.tasks.values()) {\n stats[task.status]++;\n }\n\n return stats;\n }\n}\n\n/**\n * 获取路由预加载管理器单例\n */\nlet routePreloaderInstance: RoutePreloader | null = null;\n\nexport function getRoutePreloader(config?: PreloadConfig): RoutePreloader {\n if (!routePreloaderInstance) {\n routePreloaderInstance = new RoutePreloader(config);\n }\n return routePreloaderInstance;\n}\n"],"names":["PreloadStrategy","RoutePreloader","getRoutePreloader","registerRoute","route","page","layout","priority","handle","order","config","strategy","priorityThreshold","tasks","set","name","importFn","routeName","status","children","forEach","child","registerRoutes","routes","preloadRoute","Array","from","values","filter","task","length","Promise","all","map","preloadTask","startTime","Date","now","timeoutPromise","_","reject","setTimeout","Error","timeout","race","duration","logger","debug","error","warn","startPreload","preloadTimer","executePreload","delay","sort","a","b","tasksToPreload","slice","Math","min","concurrency","i","batch","stopPreload","clearTimeout","clear","getStats","stats","total","size","pending","loading","loaded","failed","Map","routePreloaderInstance"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/core/router/performance/RoutePreloader.ts"],"sourcesContent":["/**\n * 路由预加载管理器\n * 提供路由组件的预加载功能\n */\n\nimport { logger } from '../../../utils';\nimport type { RouteConfig } from '../types';\nimport type { ComponentImport } from '../types';\nimport type { TransformRoutesResult } from '../utils/transform';\n\n/**\n * 预加载策略\n */\nexport enum PreloadStrategy {\n /**\n * 不预加载\n */\n NONE = 'none',\n\n /**\n * 预加载当前路由的下一级路由\n */\n NEXT_LEVEL = 'next-level',\n\n /**\n * 预加载所有路由\n */\n ALL = 'all',\n\n /**\n * 预加载可见路由(根据路由配置的优先级)\n */\n VISIBLE = 'visible',\n}\n\n/**\n * 预加载配置\n */\nexport interface PreloadConfig {\n /**\n * 预加载策略\n * @default PreloadStrategy.NEXT_LEVEL\n */\n strategy?: PreloadStrategy;\n\n /**\n * 预加载延迟(毫秒)\n * @default 1000\n */\n delay?: number;\n\n /**\n * 预加载优先级阈值(只预加载优先级小于等于此值的路由)\n * @default 10\n */\n priorityThreshold?: number;\n\n /**\n * 预加载超时时间(毫秒)\n * @default 5000\n */\n timeout?: number;\n}\n\n/**\n * 预加载任务\n */\ninterface PreloadTask {\n /**\n * 组件导入函数\n */\n importFn: ComponentImport;\n /**\n * 路由名称\n */\n routeName: string;\n /**\n * 优先级\n */\n priority: number;\n /**\n * 状态\n */\n status: 'pending' | 'loading' | 'loaded' | 'failed';\n /**\n * 开始时间\n */\n startTime?: number;\n}\n\n/**\n * 路由预加载管理器\n */\nexport class RoutePreloader {\n private tasks: Map<string, PreloadTask> = new Map();\n private config: Required<PreloadConfig>;\n private preloadTimer: ReturnType<typeof setTimeout> | null = null;\n private idleHandle: number | null = null;\n\n constructor(config: PreloadConfig = {}) {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NONE,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n /**\n * 更新预加载配置\n */\n updateConfig(config: PreloadConfig = {}): void {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NONE,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n private shouldRegister(priority: number): boolean {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return false;\n }\n\n if (\n this.config.strategy === PreloadStrategy.VISIBLE &&\n priority > this.config.priorityThreshold\n ) {\n return false;\n }\n\n return true;\n }\n\n private registerTask(\n key: string,\n importFn: ComponentImport,\n routeName: string,\n priority: number\n ): void {\n if (!this.shouldRegister(priority)) {\n return;\n }\n\n this.tasks.set(key, {\n importFn,\n routeName,\n priority,\n status: 'pending',\n });\n }\n\n /**\n * 注册路由用于预加载\n */\n registerRoute(route: RouteConfig): void {\n if (!route.page && !route.layout) {\n return;\n }\n\n const priority = route.handle?.order ?? 999;\n\n // 注册页面组件\n if (route.page && typeof route.page === 'function') {\n this.registerTask(`${route.name}_page`, route.page, route.name, priority);\n }\n\n // 注册布局组件\n if (route.layout && typeof route.layout === 'function') {\n this.registerTask(`${route.name}_layout`, route.layout, route.name, priority);\n }\n\n // 递归注册子路由\n if (route.children) {\n route.children.forEach((child) => this.registerRoute(child));\n }\n }\n\n /**\n * 注册多个路由\n */\n registerRoutes(\n routes: RouteConfig[],\n resolvers?: Pick<TransformRoutesResult, 'pages' | 'layouts' | 'errors' | 'loadings'>\n ): void {\n const priorityByPath = new Map<string, number>();\n const nameByPath = new Map<string, string>();\n\n const collectPriority = (route: RouteConfig): void => {\n const priority = route.handle?.order ?? 999;\n const routeName = route.name;\n\n const record = (value: unknown) => {\n if (typeof value !== 'string') {\n return;\n }\n if (!priorityByPath.has(value) || (priorityByPath.get(value) ?? 999) > priority) {\n priorityByPath.set(value, priority);\n nameByPath.set(value, routeName);\n }\n };\n\n record(route.page);\n record(route.layout);\n record(route.error ?? route.errors);\n record(route.loading);\n\n route.children?.forEach(collectPriority);\n };\n\n routes.forEach(collectPriority);\n routes.forEach((route) => this.registerRoute(route));\n\n if (!resolvers) {\n return;\n }\n\n const registerResolverMap = (\n map: Map<string, ComponentImport>,\n type: 'page' | 'layout' | 'error' | 'loading'\n ) => {\n for (const [path, importFn] of map.entries()) {\n const routeName = nameByPath.get(path) ?? path;\n const priority = priorityByPath.get(path) ?? 999;\n this.registerTask(`${routeName}_${type}_${path}`, importFn, routeName, priority);\n }\n };\n\n registerResolverMap(resolvers.pages, 'page');\n registerResolverMap(resolvers.layouts, 'layout');\n registerResolverMap(resolvers.errors, 'error');\n registerResolverMap(resolvers.loadings, 'loading');\n }\n\n /**\n * 预加载指定路由\n */\n async preloadRoute(routeName: string): Promise<void> {\n const tasks = Array.from(this.tasks.values()).filter(\n (task) => task.routeName === routeName && task.status === 'pending'\n );\n\n if (tasks.length === 0) {\n return;\n }\n\n await Promise.all(tasks.map((task) => this.preloadTask(task)));\n }\n\n /**\n * 预加载任务\n */\n private async preloadTask(task: PreloadTask): Promise<void> {\n if (task.status !== 'pending') {\n return;\n }\n\n task.status = 'loading';\n task.startTime = Date.now();\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n try {\n // 设置超时\n const timeoutPromise = new Promise((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error('预加载超时')), this.config.timeout);\n });\n\n // 执行预加载\n await Promise.race([task.importFn(), timeoutPromise]);\n\n task.status = 'loaded';\n const duration = Date.now() - (task.startTime || 0);\n logger.debug(`路由组件预加载成功: ${task.routeName} (${duration}ms)`);\n } catch (error) {\n task.status = 'failed';\n logger.warn(`路由组件预加载失败: ${task.routeName}`, error);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * 开始预加载(根据策略)\n */\n startPreload(): void {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n this.stopPreload();\n\n // 延迟预加载,避免影响初始渲染\n this.preloadTimer = setTimeout(() => {\n const browserWindow = typeof window !== 'undefined' ? (window as Window & {\n requestIdleCallback?: (callback: () => void) => number;\n cancelIdleCallback?: (id: number) => void;\n }) : undefined;\n\n if (browserWindow?.requestIdleCallback) {\n this.idleHandle = browserWindow.requestIdleCallback(() => {\n this.executePreload();\n });\n return;\n }\n\n this.executePreload();\n }, this.config.delay);\n }\n\n /**\n * 执行预加载\n */\n private async executePreload(): Promise<void> {\n const tasks = Array.from(this.tasks.values())\n .filter((task) => task.status === 'pending')\n .sort((a, b) => a.priority - b.priority);\n\n if (tasks.length === 0) {\n return;\n }\n\n logger.debug(`开始预加载 ${tasks.length} 个路由组件`);\n\n // 根据策略决定预加载哪些任务\n let tasksToPreload: PreloadTask[] = [];\n\n switch (this.config.strategy) {\n case PreloadStrategy.ALL:\n tasksToPreload = tasks;\n break;\n case PreloadStrategy.NEXT_LEVEL:\n // 只预加载优先级最低的(最可能访问的)\n tasksToPreload = tasks.slice(0, Math.min(5, tasks.length));\n break;\n case PreloadStrategy.VISIBLE:\n tasksToPreload = tasks.filter(\n (task) => task.priority <= this.config.priorityThreshold\n );\n break;\n default:\n return;\n }\n\n // 并行预加载(限制并发数)\n const concurrency = 3;\n for (let i = 0; i < tasksToPreload.length; i += concurrency) {\n const batch = tasksToPreload.slice(i, i + concurrency);\n await Promise.all(batch.map((task) => this.preloadTask(task)));\n }\n\n logger.debug('路由组件预加载完成');\n }\n\n /**\n * 停止预加载\n */\n stopPreload(): void {\n const browserWindow = typeof window !== 'undefined' ? (window as Window & {\n cancelIdleCallback?: (id: number) => void;\n }) : undefined;\n\n if (this.preloadTimer) {\n clearTimeout(this.preloadTimer);\n this.preloadTimer = null;\n }\n if (this.idleHandle !== null && browserWindow?.cancelIdleCallback) {\n browserWindow.cancelIdleCallback(this.idleHandle);\n this.idleHandle = null;\n }\n }\n\n /**\n * 清空所有任务\n */\n clear(): void {\n this.tasks.clear();\n this.stopPreload();\n logger.debug('路由预加载任务已清空');\n }\n\n /**\n * 获取预加载统计信息\n */\n getStats(): {\n total: number;\n pending: number;\n loading: number;\n loaded: number;\n failed: number;\n } {\n const stats = {\n total: this.tasks.size,\n pending: 0,\n loading: 0,\n loaded: 0,\n failed: 0,\n };\n\n for (const task of this.tasks.values()) {\n stats[task.status]++;\n }\n\n return stats;\n }\n}\n\n/**\n * 获取路由预加载管理器单例\n */\nlet routePreloaderInstance: RoutePreloader | null = null;\n\nexport function getRoutePreloader(config?: PreloadConfig): RoutePreloader {\n if (!routePreloaderInstance) {\n routePreloaderInstance = new RoutePreloader(config);\n }\n return routePreloaderInstance;\n}\n"],"names":["PreloadStrategy","RoutePreloader","getRoutePreloader","updateConfig","config","strategy","delay","priorityThreshold","timeout","shouldRegister","priority","registerTask","key","importFn","routeName","tasks","set","status","registerRoute","route","page","layout","handle","order","name","children","forEach","child","registerRoutes","routes","resolvers","priorityByPath","Map","nameByPath","collectPriority","record","value","has","get","error","errors","loading","registerResolverMap","map","type","path","entries","pages","layouts","loadings","preloadRoute","Array","from","values","filter","task","length","Promise","all","preloadTask","startTime","Date","now","timeoutId","timeoutPromise","_","reject","setTimeout","Error","race","duration","logger","debug","warn","clearTimeout","startPreload","stopPreload","preloadTimer","browserWindow","window","undefined","requestIdleCallback","idleHandle","executePreload","sort","a","b","tasksToPreload","slice","Math","min","concurrency","i","batch","cancelIdleCallback","clear","getStats","stats","total","size","pending","loaded","failed","routePreloaderInstance"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  * 提供路由组件的预加载功能
4
4
  */
5
5
  import type { RouteConfig } from '../types';
6
+ import type { TransformRoutesResult } from '../utils/transform';
6
7
  /**
7
8
  * 预加载策略
8
9
  */
@@ -56,7 +57,14 @@ export declare class RoutePreloader {
56
57
  private tasks;
57
58
  private config;
58
59
  private preloadTimer;
60
+ private idleHandle;
59
61
  constructor(config?: PreloadConfig);
62
+ /**
63
+ * 更新预加载配置
64
+ */
65
+ updateConfig(config?: PreloadConfig): void;
66
+ private shouldRegister;
67
+ private registerTask;
60
68
  /**
61
69
  * 注册路由用于预加载
62
70
  */
@@ -64,7 +72,7 @@ export declare class RoutePreloader {
64
72
  /**
65
73
  * 注册多个路由
66
74
  */
67
- registerRoutes(routes: RouteConfig[]): void;
75
+ registerRoutes(routes: RouteConfig[], resolvers?: Pick<TransformRoutesResult, 'pages' | 'layouts' | 'errors' | 'loadings'>): void;
68
76
  /**
69
77
  * 预加载指定路由
70
78
  */
@@ -36,36 +36,49 @@ function _define_property(obj, key, value) {
36
36
  * 路由预加载管理器
37
37
  */ export class RoutePreloader {
38
38
  /**
39
+ * 更新预加载配置
40
+ */ updateConfig(config = {}) {
41
+ this.config = {
42
+ strategy: config.strategy ?? "none",
43
+ delay: config.delay ?? 1000,
44
+ priorityThreshold: config.priorityThreshold ?? 10,
45
+ timeout: config.timeout ?? 5000
46
+ };
47
+ }
48
+ shouldRegister(priority) {
49
+ if (this.config.strategy === "none") {
50
+ return false;
51
+ }
52
+ if (this.config.strategy === "visible" && priority > this.config.priorityThreshold) {
53
+ return false;
54
+ }
55
+ return true;
56
+ }
57
+ registerTask(key, importFn, routeName, priority) {
58
+ if (!this.shouldRegister(priority)) {
59
+ return;
60
+ }
61
+ this.tasks.set(key, {
62
+ importFn,
63
+ routeName,
64
+ priority,
65
+ status: 'pending'
66
+ });
67
+ }
68
+ /**
39
69
  * 注册路由用于预加载
40
70
  */ registerRoute(route) {
41
71
  if (!route.page && !route.layout) {
42
72
  return;
43
73
  }
44
74
  const priority = route.handle?.order ?? 999;
45
- // 根据策略决定是否注册
46
- if (this.config.strategy === "none") {
47
- return;
48
- }
49
- if (this.config.strategy === "visible" && priority > this.config.priorityThreshold) {
50
- return;
51
- }
52
75
  // 注册页面组件
53
76
  if (route.page && typeof route.page === 'function') {
54
- this.tasks.set(`${route.name}_page`, {
55
- importFn: route.page,
56
- routeName: route.name,
57
- priority,
58
- status: 'pending'
59
- });
77
+ this.registerTask(`${route.name}_page`, route.page, route.name, priority);
60
78
  }
61
79
  // 注册布局组件
62
80
  if (route.layout && typeof route.layout === 'function') {
63
- this.tasks.set(`${route.name}_layout`, {
64
- importFn: route.layout,
65
- routeName: route.name,
66
- priority,
67
- status: 'pending'
68
- });
81
+ this.registerTask(`${route.name}_layout`, route.layout, route.name, priority);
69
82
  }
70
83
  // 递归注册子路由
71
84
  if (route.children) {
@@ -74,8 +87,43 @@ function _define_property(obj, key, value) {
74
87
  }
75
88
  /**
76
89
  * 注册多个路由
77
- */ registerRoutes(routes) {
90
+ */ registerRoutes(routes, resolvers) {
91
+ const priorityByPath = new Map();
92
+ const nameByPath = new Map();
93
+ const collectPriority = (route)=>{
94
+ const priority = route.handle?.order ?? 999;
95
+ const routeName = route.name;
96
+ const record = (value)=>{
97
+ if (typeof value !== 'string') {
98
+ return;
99
+ }
100
+ if (!priorityByPath.has(value) || (priorityByPath.get(value) ?? 999) > priority) {
101
+ priorityByPath.set(value, priority);
102
+ nameByPath.set(value, routeName);
103
+ }
104
+ };
105
+ record(route.page);
106
+ record(route.layout);
107
+ record(route.error ?? route.errors);
108
+ record(route.loading);
109
+ route.children?.forEach(collectPriority);
110
+ };
111
+ routes.forEach(collectPriority);
78
112
  routes.forEach((route)=>this.registerRoute(route));
113
+ if (!resolvers) {
114
+ return;
115
+ }
116
+ const registerResolverMap = (map, type)=>{
117
+ for (const [path, importFn] of map.entries()){
118
+ const routeName = nameByPath.get(path) ?? path;
119
+ const priority = priorityByPath.get(path) ?? 999;
120
+ this.registerTask(`${routeName}_${type}_${path}`, importFn, routeName, priority);
121
+ }
122
+ };
123
+ registerResolverMap(resolvers.pages, 'page');
124
+ registerResolverMap(resolvers.layouts, 'layout');
125
+ registerResolverMap(resolvers.errors, 'error');
126
+ registerResolverMap(resolvers.loadings, 'loading');
79
127
  }
80
128
  /**
81
129
  * 预加载指定路由
@@ -94,10 +142,11 @@ function _define_property(obj, key, value) {
94
142
  }
95
143
  task.status = 'loading';
96
144
  task.startTime = Date.now();
145
+ let timeoutId = null;
97
146
  try {
98
147
  // 设置超时
99
148
  const timeoutPromise = new Promise((_, reject)=>{
100
- setTimeout(()=>reject(new Error('预加载超时')), this.config.timeout);
149
+ timeoutId = setTimeout(()=>reject(new Error('预加载超时')), this.config.timeout);
101
150
  });
102
151
  // 执行预加载
103
152
  await Promise.race([
@@ -110,6 +159,10 @@ function _define_property(obj, key, value) {
110
159
  } catch (error) {
111
160
  task.status = 'failed';
112
161
  logger.warn(`路由组件预加载失败: ${task.routeName}`, error);
162
+ } finally{
163
+ if (timeoutId) {
164
+ clearTimeout(timeoutId);
165
+ }
113
166
  }
114
167
  }
115
168
  /**
@@ -118,8 +171,16 @@ function _define_property(obj, key, value) {
118
171
  if (this.config.strategy === "none") {
119
172
  return;
120
173
  }
174
+ this.stopPreload();
121
175
  // 延迟预加载,避免影响初始渲染
122
176
  this.preloadTimer = setTimeout(()=>{
177
+ const browserWindow = typeof window !== 'undefined' ? window : undefined;
178
+ if (browserWindow?.requestIdleCallback) {
179
+ this.idleHandle = browserWindow.requestIdleCallback(()=>{
180
+ this.executePreload();
181
+ });
182
+ return;
183
+ }
123
184
  this.executePreload();
124
185
  }, this.config.delay);
125
186
  }
@@ -158,10 +219,15 @@ function _define_property(obj, key, value) {
158
219
  /**
159
220
  * 停止预加载
160
221
  */ stopPreload() {
222
+ const browserWindow = typeof window !== 'undefined' ? window : undefined;
161
223
  if (this.preloadTimer) {
162
224
  clearTimeout(this.preloadTimer);
163
225
  this.preloadTimer = null;
164
226
  }
227
+ if (this.idleHandle !== null && browserWindow?.cancelIdleCallback) {
228
+ browserWindow.cancelIdleCallback(this.idleHandle);
229
+ this.idleHandle = null;
230
+ }
165
231
  }
166
232
  /**
167
233
  * 清空所有任务
@@ -189,8 +255,9 @@ function _define_property(obj, key, value) {
189
255
  _define_property(this, "tasks", new Map());
190
256
  _define_property(this, "config", void 0);
191
257
  _define_property(this, "preloadTimer", null);
258
+ _define_property(this, "idleHandle", null);
192
259
  this.config = {
193
- strategy: config.strategy ?? "next-level",
260
+ strategy: config.strategy ?? "none",
194
261
  delay: config.delay ?? 1000,
195
262
  priorityThreshold: config.priorityThreshold ?? 10,
196
263
  timeout: config.timeout ?? 5000
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/router/performance/RoutePreloader.ts"],"sourcesContent":["/**\n * 路由预加载管理器\n * 提供路由组件的预加载功能\n */\n\nimport { logger } from '../../../utils';\nimport type { RouteConfig } from '../types';\nimport type { ComponentImport } from '../types';\n\n/**\n * 预加载策略\n */\nexport enum PreloadStrategy {\n /**\n * 不预加载\n */\n NONE = 'none',\n\n /**\n * 预加载当前路由的下一级路由\n */\n NEXT_LEVEL = 'next-level',\n\n /**\n * 预加载所有路由\n */\n ALL = 'all',\n\n /**\n * 预加载可见路由(根据路由配置的优先级)\n */\n VISIBLE = 'visible',\n}\n\n/**\n * 预加载配置\n */\nexport interface PreloadConfig {\n /**\n * 预加载策略\n * @default PreloadStrategy.NEXT_LEVEL\n */\n strategy?: PreloadStrategy;\n\n /**\n * 预加载延迟(毫秒)\n * @default 1000\n */\n delay?: number;\n\n /**\n * 预加载优先级阈值(只预加载优先级小于等于此值的路由)\n * @default 10\n */\n priorityThreshold?: number;\n\n /**\n * 预加载超时时间(毫秒)\n * @default 5000\n */\n timeout?: number;\n}\n\n/**\n * 预加载任务\n */\ninterface PreloadTask {\n /**\n * 组件导入函数\n */\n importFn: ComponentImport;\n /**\n * 路由名称\n */\n routeName: string;\n /**\n * 优先级\n */\n priority: number;\n /**\n * 状态\n */\n status: 'pending' | 'loading' | 'loaded' | 'failed';\n /**\n * 开始时间\n */\n startTime?: number;\n}\n\n/**\n * 路由预加载管理器\n */\nexport class RoutePreloader {\n private tasks: Map<string, PreloadTask> = new Map();\n private config: Required<PreloadConfig>;\n private preloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(config: PreloadConfig = {}) {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NEXT_LEVEL,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n /**\n * 注册路由用于预加载\n */\n registerRoute(route: RouteConfig): void {\n if (!route.page && !route.layout) {\n return;\n }\n\n const priority = route.handle?.order ?? 999;\n\n // 根据策略决定是否注册\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n if (\n this.config.strategy === PreloadStrategy.VISIBLE &&\n priority > this.config.priorityThreshold\n ) {\n return;\n }\n\n // 注册页面组件\n if (route.page && typeof route.page === 'function') {\n this.tasks.set(`${route.name}_page`, {\n importFn: route.page,\n routeName: route.name,\n priority,\n status: 'pending',\n });\n }\n\n // 注册布局组件\n if (route.layout && typeof route.layout === 'function') {\n this.tasks.set(`${route.name}_layout`, {\n importFn: route.layout,\n routeName: route.name,\n priority,\n status: 'pending',\n });\n }\n\n // 递归注册子路由\n if (route.children) {\n route.children.forEach((child) => this.registerRoute(child));\n }\n }\n\n /**\n * 注册多个路由\n */\n registerRoutes(routes: RouteConfig[]): void {\n routes.forEach((route) => this.registerRoute(route));\n }\n\n /**\n * 预加载指定路由\n */\n async preloadRoute(routeName: string): Promise<void> {\n const tasks = Array.from(this.tasks.values()).filter(\n (task) => task.routeName === routeName && task.status === 'pending'\n );\n\n if (tasks.length === 0) {\n return;\n }\n\n await Promise.all(tasks.map((task) => this.preloadTask(task)));\n }\n\n /**\n * 预加载任务\n */\n private async preloadTask(task: PreloadTask): Promise<void> {\n if (task.status !== 'pending') {\n return;\n }\n\n task.status = 'loading';\n task.startTime = Date.now();\n\n try {\n // 设置超时\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('预加载超时')), this.config.timeout);\n });\n\n // 执行预加载\n await Promise.race([task.importFn(), timeoutPromise]);\n\n task.status = 'loaded';\n const duration = Date.now() - (task.startTime || 0);\n logger.debug(`路由组件预加载成功: ${task.routeName} (${duration}ms)`);\n } catch (error) {\n task.status = 'failed';\n logger.warn(`路由组件预加载失败: ${task.routeName}`, error);\n }\n }\n\n /**\n * 开始预加载(根据策略)\n */\n startPreload(): void {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n // 延迟预加载,避免影响初始渲染\n this.preloadTimer = setTimeout(() => {\n this.executePreload();\n }, this.config.delay);\n }\n\n /**\n * 执行预加载\n */\n private async executePreload(): Promise<void> {\n const tasks = Array.from(this.tasks.values())\n .filter((task) => task.status === 'pending')\n .sort((a, b) => a.priority - b.priority);\n\n if (tasks.length === 0) {\n return;\n }\n\n logger.debug(`开始预加载 ${tasks.length} 个路由组件`);\n\n // 根据策略决定预加载哪些任务\n let tasksToPreload: PreloadTask[] = [];\n\n switch (this.config.strategy) {\n case PreloadStrategy.ALL:\n tasksToPreload = tasks;\n break;\n case PreloadStrategy.NEXT_LEVEL:\n // 只预加载优先级最低的(最可能访问的)\n tasksToPreload = tasks.slice(0, Math.min(5, tasks.length));\n break;\n case PreloadStrategy.VISIBLE:\n tasksToPreload = tasks.filter(\n (task) => task.priority <= this.config.priorityThreshold\n );\n break;\n default:\n return;\n }\n\n // 并行预加载(限制并发数)\n const concurrency = 3;\n for (let i = 0; i < tasksToPreload.length; i += concurrency) {\n const batch = tasksToPreload.slice(i, i + concurrency);\n await Promise.all(batch.map((task) => this.preloadTask(task)));\n }\n\n logger.debug('路由组件预加载完成');\n }\n\n /**\n * 停止预加载\n */\n stopPreload(): void {\n if (this.preloadTimer) {\n clearTimeout(this.preloadTimer);\n this.preloadTimer = null;\n }\n }\n\n /**\n * 清空所有任务\n */\n clear(): void {\n this.tasks.clear();\n this.stopPreload();\n logger.debug('路由预加载任务已清空');\n }\n\n /**\n * 获取预加载统计信息\n */\n getStats(): {\n total: number;\n pending: number;\n loading: number;\n loaded: number;\n failed: number;\n } {\n const stats = {\n total: this.tasks.size,\n pending: 0,\n loading: 0,\n loaded: 0,\n failed: 0,\n };\n\n for (const task of this.tasks.values()) {\n stats[task.status]++;\n }\n\n return stats;\n }\n}\n\n/**\n * 获取路由预加载管理器单例\n */\nlet routePreloaderInstance: RoutePreloader | null = null;\n\nexport function getRoutePreloader(config?: PreloadConfig): RoutePreloader {\n if (!routePreloaderInstance) {\n routePreloaderInstance = new RoutePreloader(config);\n }\n return routePreloaderInstance;\n}\n"],"names":["logger","PreloadStrategy","RoutePreloader","registerRoute","route","page","layout","priority","handle","order","config","strategy","priorityThreshold","tasks","set","name","importFn","routeName","status","children","forEach","child","registerRoutes","routes","preloadRoute","Array","from","values","filter","task","length","Promise","all","map","preloadTask","startTime","Date","now","timeoutPromise","_","reject","setTimeout","Error","timeout","race","duration","debug","error","warn","startPreload","preloadTimer","executePreload","delay","sort","a","b","tasksToPreload","slice","Math","min","concurrency","i","batch","stopPreload","clearTimeout","clear","getStats","stats","total","size","pending","loading","loaded","failed","Map","routePreloaderInstance","getRoutePreloader"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/core/router/performance/RoutePreloader.ts"],"sourcesContent":["/**\n * 路由预加载管理器\n * 提供路由组件的预加载功能\n */\n\nimport { logger } from '../../../utils';\nimport type { RouteConfig } from '../types';\nimport type { ComponentImport } from '../types';\nimport type { TransformRoutesResult } from '../utils/transform';\n\n/**\n * 预加载策略\n */\nexport enum PreloadStrategy {\n /**\n * 不预加载\n */\n NONE = 'none',\n\n /**\n * 预加载当前路由的下一级路由\n */\n NEXT_LEVEL = 'next-level',\n\n /**\n * 预加载所有路由\n */\n ALL = 'all',\n\n /**\n * 预加载可见路由(根据路由配置的优先级)\n */\n VISIBLE = 'visible',\n}\n\n/**\n * 预加载配置\n */\nexport interface PreloadConfig {\n /**\n * 预加载策略\n * @default PreloadStrategy.NEXT_LEVEL\n */\n strategy?: PreloadStrategy;\n\n /**\n * 预加载延迟(毫秒)\n * @default 1000\n */\n delay?: number;\n\n /**\n * 预加载优先级阈值(只预加载优先级小于等于此值的路由)\n * @default 10\n */\n priorityThreshold?: number;\n\n /**\n * 预加载超时时间(毫秒)\n * @default 5000\n */\n timeout?: number;\n}\n\n/**\n * 预加载任务\n */\ninterface PreloadTask {\n /**\n * 组件导入函数\n */\n importFn: ComponentImport;\n /**\n * 路由名称\n */\n routeName: string;\n /**\n * 优先级\n */\n priority: number;\n /**\n * 状态\n */\n status: 'pending' | 'loading' | 'loaded' | 'failed';\n /**\n * 开始时间\n */\n startTime?: number;\n}\n\n/**\n * 路由预加载管理器\n */\nexport class RoutePreloader {\n private tasks: Map<string, PreloadTask> = new Map();\n private config: Required<PreloadConfig>;\n private preloadTimer: ReturnType<typeof setTimeout> | null = null;\n private idleHandle: number | null = null;\n\n constructor(config: PreloadConfig = {}) {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NONE,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n /**\n * 更新预加载配置\n */\n updateConfig(config: PreloadConfig = {}): void {\n this.config = {\n strategy: config.strategy ?? PreloadStrategy.NONE,\n delay: config.delay ?? 1000,\n priorityThreshold: config.priorityThreshold ?? 10,\n timeout: config.timeout ?? 5000,\n };\n }\n\n private shouldRegister(priority: number): boolean {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return false;\n }\n\n if (\n this.config.strategy === PreloadStrategy.VISIBLE &&\n priority > this.config.priorityThreshold\n ) {\n return false;\n }\n\n return true;\n }\n\n private registerTask(\n key: string,\n importFn: ComponentImport,\n routeName: string,\n priority: number\n ): void {\n if (!this.shouldRegister(priority)) {\n return;\n }\n\n this.tasks.set(key, {\n importFn,\n routeName,\n priority,\n status: 'pending',\n });\n }\n\n /**\n * 注册路由用于预加载\n */\n registerRoute(route: RouteConfig): void {\n if (!route.page && !route.layout) {\n return;\n }\n\n const priority = route.handle?.order ?? 999;\n\n // 注册页面组件\n if (route.page && typeof route.page === 'function') {\n this.registerTask(`${route.name}_page`, route.page, route.name, priority);\n }\n\n // 注册布局组件\n if (route.layout && typeof route.layout === 'function') {\n this.registerTask(`${route.name}_layout`, route.layout, route.name, priority);\n }\n\n // 递归注册子路由\n if (route.children) {\n route.children.forEach((child) => this.registerRoute(child));\n }\n }\n\n /**\n * 注册多个路由\n */\n registerRoutes(\n routes: RouteConfig[],\n resolvers?: Pick<TransformRoutesResult, 'pages' | 'layouts' | 'errors' | 'loadings'>\n ): void {\n const priorityByPath = new Map<string, number>();\n const nameByPath = new Map<string, string>();\n\n const collectPriority = (route: RouteConfig): void => {\n const priority = route.handle?.order ?? 999;\n const routeName = route.name;\n\n const record = (value: unknown) => {\n if (typeof value !== 'string') {\n return;\n }\n if (!priorityByPath.has(value) || (priorityByPath.get(value) ?? 999) > priority) {\n priorityByPath.set(value, priority);\n nameByPath.set(value, routeName);\n }\n };\n\n record(route.page);\n record(route.layout);\n record(route.error ?? route.errors);\n record(route.loading);\n\n route.children?.forEach(collectPriority);\n };\n\n routes.forEach(collectPriority);\n routes.forEach((route) => this.registerRoute(route));\n\n if (!resolvers) {\n return;\n }\n\n const registerResolverMap = (\n map: Map<string, ComponentImport>,\n type: 'page' | 'layout' | 'error' | 'loading'\n ) => {\n for (const [path, importFn] of map.entries()) {\n const routeName = nameByPath.get(path) ?? path;\n const priority = priorityByPath.get(path) ?? 999;\n this.registerTask(`${routeName}_${type}_${path}`, importFn, routeName, priority);\n }\n };\n\n registerResolverMap(resolvers.pages, 'page');\n registerResolverMap(resolvers.layouts, 'layout');\n registerResolverMap(resolvers.errors, 'error');\n registerResolverMap(resolvers.loadings, 'loading');\n }\n\n /**\n * 预加载指定路由\n */\n async preloadRoute(routeName: string): Promise<void> {\n const tasks = Array.from(this.tasks.values()).filter(\n (task) => task.routeName === routeName && task.status === 'pending'\n );\n\n if (tasks.length === 0) {\n return;\n }\n\n await Promise.all(tasks.map((task) => this.preloadTask(task)));\n }\n\n /**\n * 预加载任务\n */\n private async preloadTask(task: PreloadTask): Promise<void> {\n if (task.status !== 'pending') {\n return;\n }\n\n task.status = 'loading';\n task.startTime = Date.now();\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n try {\n // 设置超时\n const timeoutPromise = new Promise((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error('预加载超时')), this.config.timeout);\n });\n\n // 执行预加载\n await Promise.race([task.importFn(), timeoutPromise]);\n\n task.status = 'loaded';\n const duration = Date.now() - (task.startTime || 0);\n logger.debug(`路由组件预加载成功: ${task.routeName} (${duration}ms)`);\n } catch (error) {\n task.status = 'failed';\n logger.warn(`路由组件预加载失败: ${task.routeName}`, error);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * 开始预加载(根据策略)\n */\n startPreload(): void {\n if (this.config.strategy === PreloadStrategy.NONE) {\n return;\n }\n\n this.stopPreload();\n\n // 延迟预加载,避免影响初始渲染\n this.preloadTimer = setTimeout(() => {\n const browserWindow = typeof window !== 'undefined' ? (window as Window & {\n requestIdleCallback?: (callback: () => void) => number;\n cancelIdleCallback?: (id: number) => void;\n }) : undefined;\n\n if (browserWindow?.requestIdleCallback) {\n this.idleHandle = browserWindow.requestIdleCallback(() => {\n this.executePreload();\n });\n return;\n }\n\n this.executePreload();\n }, this.config.delay);\n }\n\n /**\n * 执行预加载\n */\n private async executePreload(): Promise<void> {\n const tasks = Array.from(this.tasks.values())\n .filter((task) => task.status === 'pending')\n .sort((a, b) => a.priority - b.priority);\n\n if (tasks.length === 0) {\n return;\n }\n\n logger.debug(`开始预加载 ${tasks.length} 个路由组件`);\n\n // 根据策略决定预加载哪些任务\n let tasksToPreload: PreloadTask[] = [];\n\n switch (this.config.strategy) {\n case PreloadStrategy.ALL:\n tasksToPreload = tasks;\n break;\n case PreloadStrategy.NEXT_LEVEL:\n // 只预加载优先级最低的(最可能访问的)\n tasksToPreload = tasks.slice(0, Math.min(5, tasks.length));\n break;\n case PreloadStrategy.VISIBLE:\n tasksToPreload = tasks.filter(\n (task) => task.priority <= this.config.priorityThreshold\n );\n break;\n default:\n return;\n }\n\n // 并行预加载(限制并发数)\n const concurrency = 3;\n for (let i = 0; i < tasksToPreload.length; i += concurrency) {\n const batch = tasksToPreload.slice(i, i + concurrency);\n await Promise.all(batch.map((task) => this.preloadTask(task)));\n }\n\n logger.debug('路由组件预加载完成');\n }\n\n /**\n * 停止预加载\n */\n stopPreload(): void {\n const browserWindow = typeof window !== 'undefined' ? (window as Window & {\n cancelIdleCallback?: (id: number) => void;\n }) : undefined;\n\n if (this.preloadTimer) {\n clearTimeout(this.preloadTimer);\n this.preloadTimer = null;\n }\n if (this.idleHandle !== null && browserWindow?.cancelIdleCallback) {\n browserWindow.cancelIdleCallback(this.idleHandle);\n this.idleHandle = null;\n }\n }\n\n /**\n * 清空所有任务\n */\n clear(): void {\n this.tasks.clear();\n this.stopPreload();\n logger.debug('路由预加载任务已清空');\n }\n\n /**\n * 获取预加载统计信息\n */\n getStats(): {\n total: number;\n pending: number;\n loading: number;\n loaded: number;\n failed: number;\n } {\n const stats = {\n total: this.tasks.size,\n pending: 0,\n loading: 0,\n loaded: 0,\n failed: 0,\n };\n\n for (const task of this.tasks.values()) {\n stats[task.status]++;\n }\n\n return stats;\n }\n}\n\n/**\n * 获取路由预加载管理器单例\n */\nlet routePreloaderInstance: RoutePreloader | null = null;\n\nexport function getRoutePreloader(config?: PreloadConfig): RoutePreloader {\n if (!routePreloaderInstance) {\n routePreloaderInstance = new RoutePreloader(config);\n }\n return routePreloaderInstance;\n}\n"],"names":["logger","PreloadStrategy","RoutePreloader","updateConfig","config","strategy","delay","priorityThreshold","timeout","shouldRegister","priority","registerTask","key","importFn","routeName","tasks","set","status","registerRoute","route","page","layout","handle","order","name","children","forEach","child","registerRoutes","routes","resolvers","priorityByPath","Map","nameByPath","collectPriority","record","value","has","get","error","errors","loading","registerResolverMap","map","type","path","entries","pages","layouts","loadings","preloadRoute","Array","from","values","filter","task","length","Promise","all","preloadTask","startTime","Date","now","timeoutId","timeoutPromise","_","reject","setTimeout","Error","race","duration","debug","warn","clearTimeout","startPreload","stopPreload","preloadTimer","browserWindow","window","undefined","requestIdleCallback","idleHandle","executePreload","sort","a","b","tasksToPreload","slice","Math","min","concurrency","i","batch","cancelIdleCallback","clear","getStats","stats","total","size","pending","loaded","failed","routePreloaderInstance","getRoutePreloader"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { AppInstance } from '../startup/AppInstance';
2
2
  import type { createBrowserRouter, createMemoryRouter } from 'react-router-dom';
3
3
  import type { TransformOptions } from './utils/transform';
4
+ import type { PreloadConfig } from './performance/RoutePreloader';
4
5
  export type RouteItemHandle = {
5
6
  title: string;
6
7
  i18nKey?: string;
@@ -19,6 +20,7 @@ export type RouteItem = {
19
20
  page?: string | ComponentImport | null;
20
21
  loading?: string | ComponentImport | null;
21
22
  errors?: string | ComponentImport | null;
23
+ error?: string | ComponentImport | null;
22
24
  name: string;
23
25
  path: string | undefined;
24
26
  isGroup?: boolean;
@@ -93,7 +95,7 @@ export type ComponentImport = () => Promise<any>;
93
95
  * 路由配置项
94
96
  *
95
97
  * 与 RouteItem 的区别:
96
- * - layout、page、loading、errors 可以是组件路径字符串(如 "@/pages/index.ts")或动态导入函数
98
+ * - layout、page、loading、error/errors 可以是组件路径字符串(如 "@/pages/index.ts")或动态导入函数
97
99
  * - 如果使用字符串,系统会自动转换为动态导入函数
98
100
  * - 如果使用函数,则直接使用,无需转换
99
101
  * - 对应的组件必须默认导出(export default xxxx)
@@ -131,6 +133,14 @@ export interface RouteConfig {
131
133
  * 组件必须默认导出
132
134
  */
133
135
  errors?: string | ComponentImport | null;
136
+ /**
137
+ * 错误组件路径(字符串)或动态导入函数(兼容字段,优先级高于 errors)
138
+ *
139
+ * 字符串示例: "@/pages/error.tsx"
140
+ * 函数示例: () => import("@/pages/error.tsx")
141
+ * 组件必须默认导出
142
+ */
143
+ error?: string | ComponentImport | null;
134
144
  /**
135
145
  * 路由名称(唯一标识)
136
146
  */
@@ -330,7 +340,7 @@ export interface RouterConfig {
330
340
  *
331
341
  * 使用 RouteConfig 类型
332
342
  *
333
- * 注意:RouteConfig 中的 layout、page、loading、errors 可以是:
343
+ * 注意:RouteConfig 中的 layout、page、loading、error/errors 可以是:
334
344
  * - 组件路径字符串:系统会自动转换为动态导入函数
335
345
  * - 动态导入函数:直接使用,无需转换
336
346
  *
@@ -345,7 +355,7 @@ export interface RouterConfig {
345
355
  *
346
356
  * 路径解析由构建工具(如 Vite、Webpack)处理,框架会在运行时验证组件是否有默认导出。
347
357
  */
348
- routes?: RouteConfig[] | (() => Promise<RouteConfig[]>);
358
+ routes?: RouteConfig[] | (() => RouteConfig[] | Promise<RouteConfig[]>);
349
359
  /**
350
360
  * 路由模式
351
361
  *
@@ -377,6 +387,13 @@ export interface RouterConfig {
377
387
  * 用于配置转换选项,主要用于转换路由配置。
378
388
  */
379
389
  transformOptions?: TransformOptions;
390
+ /**
391
+ * 路由预加载配置
392
+ *
393
+ * 默认策略为 `none`,即按需懒加载路由组件。
394
+ * 如需预加载,可显式配置 `strategy` 为 `next-level` / `visible` / `all`。
395
+ */
396
+ preload?: PreloadConfig;
380
397
  /**
381
398
  * 路由生命周期钩子
382
399
  */
@@ -390,7 +407,7 @@ export interface RouterConfig {
390
407
  /**
391
408
  * 默认路由错误组件
392
409
  *
393
- * 当路由配置中的 `errors` 为空时,将使用此组件作为错误边界。
410
+ * 当路由配置中的 `error`/`errors` 为空时,将使用此组件作为错误边界。
394
411
  * 如果不提供,将使用框架内置的 `RouteErrorBoundary` 组件。
395
412
  *
396
413
  * 组件必须使用默认导出(export default)。
@@ -404,7 +421,7 @@ export interface RouterConfig {
404
421
  * name: 'home',
405
422
  * path: '/',
406
423
  * page: './pages/Home',
407
- * // errors 为空时,将使用 defaultRouteErrorComponent
424
+ * // error/errors 为空时,将使用 defaultRouteErrorComponent
408
425
  * },
409
426
  * ],
410
427
  * },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/router/types.ts"],"sourcesContent":["import type { AppInstance } from '../startup/AppInstance';\nimport type { createBrowserRouter, createMemoryRouter } from 'react-router-dom';\nimport type { TransformOptions } from './utils/transform';\n\nexport type RouteItemHandle = {\n title: string,\n i18nKey?: string,\n order: number,\n icon?: string,\n // 在菜单中隐藏\n hideInMenu?: boolean,\n // 隐藏尾部\n hideFooter?: boolean,\n // 保活\n keepAlive?: boolean,\n // 是否需要的登录\n needLogin?: boolean,\n // 角色\n roles?: Array<string>,\n // 路由名称,唯一Key\n name?: string,\n // 其他\n [key: string]: unknown,\n}\nexport type RouteItem = {\n // 布局 布局对象的key或动态导入函数\n layout?: string | ComponentImport | null,\n // 页面 页面对象的key或动态导入函数\n page?: string | ComponentImport | null,\n // loading 加载对象的key或动态导入函数\n loading?: string | ComponentImport | null,\n // 错误 组件的key或动态导入函数\n errors?: string | ComponentImport | null,\n // 路由名称,唯一Key\n name: string,\n // 路由\n path: string | undefined,\n // 是否分组路由\n isGroup?: boolean,\n // 是否开启重定向\n enableRedirection?: boolean,\n // 额外参数\n handle: RouteItemHandle,\n children?: RouteItem[]\n}\n\nexport type RouteMapType = Record<string, () => Promise<any>>;\n\n/**\n * DOM 路由选项类型(从 createBrowserRouter 和 createHashRouter 的参数类型提取)\n */\nexport type DOMRouterOpts = Parameters<typeof createBrowserRouter>[1];\n\n/**\n * Memory 路由选项类型(从 createMemoryRouter 的参数类型提取)\n */\nexport type MemoryRouterOpts = Parameters<typeof createMemoryRouter>[1];\n\n/**\n * 路由元数据(兼容 RouteItemHandle)\n * \n * @deprecated 建议直接使用 RouteItem 和 RouteItemHandle\n */\nexport interface RouteMeta {\n /**\n * 路由标题\n */\n title?: string;\n\n /**\n * 图标\n */\n icon?: string;\n\n /**\n * 排序\n */\n order?: number;\n\n /**\n * 权限标识\n */\n auth?: string | string[];\n\n /**\n * 是否在菜单中隐藏\n */\n hideInMenu?: boolean;\n\n /**\n * 是否启用 KeepAlive\n */\n keepAlive?: boolean;\n\n /**\n * 是否需要登录\n */\n needLogin?: boolean;\n\n /**\n * 角色列表\n */\n roles?: string[];\n\n /**\n * 路由名称(唯一标识)\n */\n name?: string;\n\n /**\n * 其他自定义元数据\n */\n [key: string]: unknown;\n}\n\n/**\n * 动态导入函数类型\n * \n * 用于懒加载组件,返回一个 Promise,resolve 后的值为组件模块对象\n * 组件必须使用默认导出(export default)\n */\nexport type ComponentImport = () => Promise<any>;\n\n/**\n * 路由配置项\n * \n * 与 RouteItem 的区别:\n * - layout、page、loading、errors 可以是组件路径字符串(如 \"@/pages/index.ts\")或动态导入函数\n * - 如果使用字符串,系统会自动转换为动态导入函数\n * - 如果使用函数,则直接使用,无需转换\n * - 对应的组件必须默认导出(export default xxxx)\n */\nexport interface RouteConfig {\n /**\n * 布局组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/layout.tsx\" 或 \"../../pages/layout.tsx\"\n * 函数示例: () => import(\"@/pages/layout.tsx\")\n * 组件必须默认导出\n */\n layout?: string | ComponentImport | null;\n\n /**\n * 页面组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/index.tsx\" 或 \"../../pages/index.tsx\"\n * 函数示例: () => import(\"@/pages/index.tsx\")\n * 组件必须默认导出\n */\n page?: string | ComponentImport | null;\n\n /**\n * 加载组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/components/Loading.tsx\"\n * 函数示例: () => import(\"@/components/Loading.tsx\")\n * 组件必须默认导出\n */\n loading?: string | ComponentImport | null;\n\n /**\n * 错误组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/error.tsx\"\n * 函数示例: () => import(\"@/pages/error.tsx\")\n * 组件必须默认导出\n */\n errors?: string | ComponentImport | null;\n\n /**\n * 路由名称(唯一标识)\n */\n name: string;\n\n /**\n * 路由路径\n */\n path: string | undefined;\n\n /**\n * 是否为路由组\n */\n isGroup?: boolean;\n\n /**\n * 是否启用重定向\n */\n enableRedirection?: boolean;\n\n /**\n * 路由元数据\n */\n handle: {\n title: string;\n i18nKey?: string;\n order: number;\n icon?: string;\n hideInMenu?: boolean;\n hideFooter?: boolean;\n keepAlive?: boolean;\n needLogin?: boolean;\n roles?: Array<string>;\n name?: string;\n [key: string]: unknown;\n };\n\n /**\n * 子路由配置\n */\n children?: RouteConfig[];\n}\n\n/**\n * 路由位置信息\n */\nexport interface RouteLocation {\n /**\n * 路径\n */\n path: string;\n\n /**\n * 路径参数\n */\n params?: Record<string, unknown>;\n\n /**\n * 查询参数\n */\n query?: Record<string, unknown>;\n\n /**\n * 路由元数据\n */\n meta?: RouteMeta;\n}\n\n/**\n * 导航选项\n */\nexport interface NavigateOptions {\n /**\n * 是否替换当前历史记录\n */\n replace?: boolean;\n\n /**\n * 状态数据\n */\n state?: unknown;\n\n /**\n * 查询参数\n */\n query?: Record<string, unknown>;\n}\n\n/**\n * 路由生命周期钩子\n */\nexport interface RouteLifecycleHooks {\n /**\n * 路由跳转前钩子\n * \n * @param to - 目标路由\n * @param from - 来源路由\n * @returns 如果返回字符串,则跳转到该路径;如果返回 false,则阻止跳转;返回 true 或 undefined 则继续跳转\n */\n beforeEach?: (to: RouteLocation, from?: RouteLocation) => string | false | void | Promise<string | false | void>;\n\n /**\n * 路由跳转后钩子\n * \n * @param to - 目标路由\n * @param from - 来源路由\n */\n afterEach?: (to: RouteLocation, from?: RouteLocation) => void | Promise<void>;\n}\n\n/**\n * 路由模式\n * \n * 对应 react-router-dom 的路由模式:\n * - `browser`: 使用 BrowserRouter(createBrowserRouter)\n * - `hash`: 使用 HashRouter(createHashRouter)\n * - `memory`: 使用 MemoryRouter(createMemoryRouter)\n */\nexport type RouterMode = 'browser' | 'hash' | 'memory';\n\n/**\n * 应用路由接口\n * \n * 这是框架核心依赖的抽象接口,所有路由实现都必须实现此接口\n */\nexport interface AppRouter {\n /**\n * 挂载到应用实例\n * \n * @param app - 应用实例\n */\n mount(app: AppInstance): void;\n\n /**\n * 路由跳转\n * \n * @param to - 目标路径或路由名称\n * @param options - 导航选项\n */\n navigate(to: string, options?: NavigateOptions): void;\n\n /**\n * 获取当前路由信息\n * \n * @returns 当前路由位置信息\n */\n getCurrentRoute(): RouteLocation;\n\n /**\n * 销毁路由实例(可选)\n */\n destroy?(): void;\n}\n\n/**\n * 路径解析配置\n */\nexport interface PathResolveConfig {\n /**\n * 项目根目录路径\n * \n * 用于解析相对路径的基准目录。\n * 如果不提供,将使用调用框架的项目根目录(通常为 process.cwd())。\n * \n * 注意:路径解析主要由构建工具(如 Vite、Webpack)处理,\n * 此配置主要用于文档和错误提示。\n * \n * @example\n * ```typescript\n * router: {\n * pathResolve: {\n * basePath: '/path/to/project',\n * },\n * }\n * ```\n */\n basePath?: string;\n\n /**\n * 路径别名映射\n * \n * 用于将路径别名(如 @/)映射到实际路径。\n * 如果不提供,将使用构建工具的配置(如 vite.config.ts 中的 resolve.alias)。\n * \n * 注意:路径别名解析主要由构建工具处理,\n * 此配置主要用于文档和错误提示。\n * \n * @example\n * ```typescript\n * router: {\n * pathResolve: {\n * pathAliases: {\n * '@': './src',\n * '@components': './src/components',\n * },\n * },\n * }\n * ```\n */\n pathAliases?: Record<string, string>;\n}\n\n/**\n * 路由配置选项\n */\nexport interface RouterConfig {\n /**\n * 是否启用路由\n * \n * - `true`: 启用内置路由(默认)\n * - `false`: 禁用内置路由,使用自定义路由\n * - `'disabled'`: 完全禁用路由\n * \n * @default true\n */\n enabled?: boolean | 'disabled';\n\n /**\n * 路由配置列表\n * \n * 使用 RouteConfig 类型\n * \n * 注意:RouteConfig 中的 layout、page、loading、errors 可以是:\n * - 组件路径字符串:系统会自动转换为动态导入函数\n * - 动态导入函数:直接使用,无需转换\n * \n * 路径字符串可以是:\n * - 相对路径:如 `\"./pages/index.tsx\"`、`\"../components/Layout.tsx\"`\n * - 路径别名:如 `\"@/pages/index.tsx\"`(需要在构建工具中配置,如 vite.config.ts)\n * - 绝对路径:如 `\"/src/pages/index.tsx\"`(不推荐)\n * \n * 动态导入函数示例:\n * - `() => import(\"./pages/index.tsx\")`\n * - `() => import(\"@/pages/index.tsx\")`\n * \n * 路径解析由构建工具(如 Vite、Webpack)处理,框架会在运行时验证组件是否有默认导出。\n */\n routes?: RouteConfig[] | (() => Promise<RouteConfig[]>);\n\n /**\n * 路由模式\n * \n * - `browser`: 使用 BrowserRouter(createBrowserRouter)- 默认\n * - `hash`: 使用 HashRouter(createHashRouter)\n * - `memory`: 使用 MemoryRouter(createMemoryRouter)\n * \n * @default 'browser'\n */\n mode?: RouterMode;\n\n /**\n * 路由选项\n * \n * 传递给 react-router-dom 的路由创建方法的选项\n * - `browser` 和 `hash` 模式使用 `DOMRouterOpts`\n * - `memory` 模式使用 `MemoryRouterOpts`\n */\n options?: DOMRouterOpts | MemoryRouterOpts;\n\n /**\n * 路径解析配置\n * \n * 用于配置路径解析选项,主要用于文档和错误提示。\n * 实际的路径解析由构建工具(如 Vite、Webpack)处理。\n */\n pathResolve?: PathResolveConfig;\n\n /**\n * 转换选项\n * \n * 用于配置转换选项,主要用于转换路由配置。\n */\n transformOptions?: TransformOptions;\n\n /**\n * 路由生命周期钩子\n */\n hooks?: RouteLifecycleHooks;\n\n /**\n * 是否启用配置验证(默认 true)\n * \n * 如果为 false,将跳过路由配置的 Zod 验证\n */\n enableValidation?: boolean;\n\n /**\n * 默认路由错误组件\n * \n * 当路由配置中的 `errors` 为空时,将使用此组件作为错误边界。\n * 如果不提供,将使用框架内置的 `RouteErrorBoundary` 组件。\n * \n * 组件必须使用默认导出(export default)。\n * \n * @example\n * ```typescript\n * router: {\n * defaultRouteErrorComponent: () => import('./components/CustomErrorBoundary'),\n * routes: [\n * {\n * name: 'home',\n * path: '/',\n * page: './pages/Home',\n * // errors 为空时,将使用 defaultRouteErrorComponent\n * },\n * ],\n * },\n * ```\n */\n defaultRouteErrorComponent?: ComponentImport;\n\n /**\n * 默认路由加载组件\n * \n * 当路由配置中的 `loading` 为空时,将使用此组件作为加载指示器。\n * 如果不提供,将使用框架内置 Loading 组件。\n * \n * 组件必须使用默认导出(export default)。\n * \n * @example\n * ```typescript\n * router: {\n * defaultRouteLoadingComponent: () => import('./components/CustomLoading'),\n * routes: [\n * {\n * name: 'home',\n * path: '/',\n * page: './pages/Home',\n * // loading 为空时,将使用 defaultRouteLoadingComponent\n * },\n * ],\n * },\n * ```\n */\n defaultRouteLoadingComponent?: ComponentImport;\n}\n"],"names":[],"mappings":"AAmXA;;CAEC,GACD,WAkIC"}
1
+ {"version":3,"sources":["../../../src/core/router/types.ts"],"sourcesContent":["import type { AppInstance } from '../startup/AppInstance';\nimport type { createBrowserRouter, createMemoryRouter } from 'react-router-dom';\nimport type { TransformOptions } from './utils/transform';\nimport type { PreloadConfig } from './performance/RoutePreloader';\n\nexport type RouteItemHandle = {\n title: string,\n i18nKey?: string,\n order: number,\n icon?: string,\n // 在菜单中隐藏\n hideInMenu?: boolean,\n // 隐藏尾部\n hideFooter?: boolean,\n // 保活\n keepAlive?: boolean,\n // 是否需要的登录\n needLogin?: boolean,\n // 角色\n roles?: Array<string>,\n // 路由名称,唯一Key\n name?: string,\n // 其他\n [key: string]: unknown,\n}\nexport type RouteItem = {\n // 布局 布局对象的key或动态导入函数\n layout?: string | ComponentImport | null,\n // 页面 页面对象的key或动态导入函数\n page?: string | ComponentImport | null,\n // loading 加载对象的key或动态导入函数\n loading?: string | ComponentImport | null,\n // 错误 组件的key或动态导入函数\n errors?: string | ComponentImport | null,\n // 错误(兼容字段)组件的key或动态导入函数\n error?: string | ComponentImport | null,\n // 路由名称,唯一Key\n name: string,\n // 路由\n path: string | undefined,\n // 是否分组路由\n isGroup?: boolean,\n // 是否开启重定向\n enableRedirection?: boolean,\n // 额外参数\n handle: RouteItemHandle,\n children?: RouteItem[]\n}\n\nexport type RouteMapType = Record<string, () => Promise<any>>;\n\n/**\n * DOM 路由选项类型(从 createBrowserRouter 和 createHashRouter 的参数类型提取)\n */\nexport type DOMRouterOpts = Parameters<typeof createBrowserRouter>[1];\n\n/**\n * Memory 路由选项类型(从 createMemoryRouter 的参数类型提取)\n */\nexport type MemoryRouterOpts = Parameters<typeof createMemoryRouter>[1];\n\n/**\n * 路由元数据(兼容 RouteItemHandle)\n * \n * @deprecated 建议直接使用 RouteItem 和 RouteItemHandle\n */\nexport interface RouteMeta {\n /**\n * 路由标题\n */\n title?: string;\n\n /**\n * 图标\n */\n icon?: string;\n\n /**\n * 排序\n */\n order?: number;\n\n /**\n * 权限标识\n */\n auth?: string | string[];\n\n /**\n * 是否在菜单中隐藏\n */\n hideInMenu?: boolean;\n\n /**\n * 是否启用 KeepAlive\n */\n keepAlive?: boolean;\n\n /**\n * 是否需要登录\n */\n needLogin?: boolean;\n\n /**\n * 角色列表\n */\n roles?: string[];\n\n /**\n * 路由名称(唯一标识)\n */\n name?: string;\n\n /**\n * 其他自定义元数据\n */\n [key: string]: unknown;\n}\n\n/**\n * 动态导入函数类型\n * \n * 用于懒加载组件,返回一个 Promise,resolve 后的值为组件模块对象\n * 组件必须使用默认导出(export default)\n */\nexport type ComponentImport = () => Promise<any>;\n\n/**\n * 路由配置项\n * \n * 与 RouteItem 的区别:\n * - layout、page、loading、error/errors 可以是组件路径字符串(如 \"@/pages/index.ts\")或动态导入函数\n * - 如果使用字符串,系统会自动转换为动态导入函数\n * - 如果使用函数,则直接使用,无需转换\n * - 对应的组件必须默认导出(export default xxxx)\n */\nexport interface RouteConfig {\n /**\n * 布局组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/layout.tsx\" 或 \"../../pages/layout.tsx\"\n * 函数示例: () => import(\"@/pages/layout.tsx\")\n * 组件必须默认导出\n */\n layout?: string | ComponentImport | null;\n\n /**\n * 页面组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/index.tsx\" 或 \"../../pages/index.tsx\"\n * 函数示例: () => import(\"@/pages/index.tsx\")\n * 组件必须默认导出\n */\n page?: string | ComponentImport | null;\n\n /**\n * 加载组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/components/Loading.tsx\"\n * 函数示例: () => import(\"@/components/Loading.tsx\")\n * 组件必须默认导出\n */\n loading?: string | ComponentImport | null;\n\n /**\n * 错误组件路径(字符串)或动态导入函数\n * \n * 字符串示例: \"@/pages/error.tsx\"\n * 函数示例: () => import(\"@/pages/error.tsx\")\n * 组件必须默认导出\n */\n errors?: string | ComponentImport | null;\n\n /**\n * 错误组件路径(字符串)或动态导入函数(兼容字段,优先级高于 errors)\n *\n * 字符串示例: \"@/pages/error.tsx\"\n * 函数示例: () => import(\"@/pages/error.tsx\")\n * 组件必须默认导出\n */\n error?: string | ComponentImport | null;\n\n /**\n * 路由名称(唯一标识)\n */\n name: string;\n\n /**\n * 路由路径\n */\n path: string | undefined;\n\n /**\n * 是否为路由组\n */\n isGroup?: boolean;\n\n /**\n * 是否启用重定向\n */\n enableRedirection?: boolean;\n\n /**\n * 路由元数据\n */\n handle: {\n title: string;\n i18nKey?: string;\n order: number;\n icon?: string;\n hideInMenu?: boolean;\n hideFooter?: boolean;\n keepAlive?: boolean;\n needLogin?: boolean;\n roles?: Array<string>;\n name?: string;\n [key: string]: unknown;\n };\n\n /**\n * 子路由配置\n */\n children?: RouteConfig[];\n}\n\n/**\n * 路由位置信息\n */\nexport interface RouteLocation {\n /**\n * 路径\n */\n path: string;\n\n /**\n * 路径参数\n */\n params?: Record<string, unknown>;\n\n /**\n * 查询参数\n */\n query?: Record<string, unknown>;\n\n /**\n * 路由元数据\n */\n meta?: RouteMeta;\n}\n\n/**\n * 导航选项\n */\nexport interface NavigateOptions {\n /**\n * 是否替换当前历史记录\n */\n replace?: boolean;\n\n /**\n * 状态数据\n */\n state?: unknown;\n\n /**\n * 查询参数\n */\n query?: Record<string, unknown>;\n}\n\n/**\n * 路由生命周期钩子\n */\nexport interface RouteLifecycleHooks {\n /**\n * 路由跳转前钩子\n * \n * @param to - 目标路由\n * @param from - 来源路由\n * @returns 如果返回字符串,则跳转到该路径;如果返回 false,则阻止跳转;返回 true 或 undefined 则继续跳转\n */\n beforeEach?: (to: RouteLocation, from?: RouteLocation) => string | false | void | Promise<string | false | void>;\n\n /**\n * 路由跳转后钩子\n * \n * @param to - 目标路由\n * @param from - 来源路由\n */\n afterEach?: (to: RouteLocation, from?: RouteLocation) => void | Promise<void>;\n}\n\n/**\n * 路由模式\n * \n * 对应 react-router-dom 的路由模式:\n * - `browser`: 使用 BrowserRouter(createBrowserRouter)\n * - `hash`: 使用 HashRouter(createHashRouter)\n * - `memory`: 使用 MemoryRouter(createMemoryRouter)\n */\nexport type RouterMode = 'browser' | 'hash' | 'memory';\n\n/**\n * 应用路由接口\n * \n * 这是框架核心依赖的抽象接口,所有路由实现都必须实现此接口\n */\nexport interface AppRouter {\n /**\n * 挂载到应用实例\n * \n * @param app - 应用实例\n */\n mount(app: AppInstance): void;\n\n /**\n * 路由跳转\n * \n * @param to - 目标路径或路由名称\n * @param options - 导航选项\n */\n navigate(to: string, options?: NavigateOptions): void;\n\n /**\n * 获取当前路由信息\n * \n * @returns 当前路由位置信息\n */\n getCurrentRoute(): RouteLocation;\n\n /**\n * 销毁路由实例(可选)\n */\n destroy?(): void;\n}\n\n/**\n * 路径解析配置\n */\nexport interface PathResolveConfig {\n /**\n * 项目根目录路径\n * \n * 用于解析相对路径的基准目录。\n * 如果不提供,将使用调用框架的项目根目录(通常为 process.cwd())。\n * \n * 注意:路径解析主要由构建工具(如 Vite、Webpack)处理,\n * 此配置主要用于文档和错误提示。\n * \n * @example\n * ```typescript\n * router: {\n * pathResolve: {\n * basePath: '/path/to/project',\n * },\n * }\n * ```\n */\n basePath?: string;\n\n /**\n * 路径别名映射\n * \n * 用于将路径别名(如 @/)映射到实际路径。\n * 如果不提供,将使用构建工具的配置(如 vite.config.ts 中的 resolve.alias)。\n * \n * 注意:路径别名解析主要由构建工具处理,\n * 此配置主要用于文档和错误提示。\n * \n * @example\n * ```typescript\n * router: {\n * pathResolve: {\n * pathAliases: {\n * '@': './src',\n * '@components': './src/components',\n * },\n * },\n * }\n * ```\n */\n pathAliases?: Record<string, string>;\n}\n\n/**\n * 路由配置选项\n */\nexport interface RouterConfig {\n /**\n * 是否启用路由\n * \n * - `true`: 启用内置路由(默认)\n * - `false`: 禁用内置路由,使用自定义路由\n * - `'disabled'`: 完全禁用路由\n * \n * @default true\n */\n enabled?: boolean | 'disabled';\n\n /**\n * 路由配置列表\n * \n * 使用 RouteConfig 类型\n * \n * 注意:RouteConfig 中的 layout、page、loading、error/errors 可以是:\n * - 组件路径字符串:系统会自动转换为动态导入函数\n * - 动态导入函数:直接使用,无需转换\n * \n * 路径字符串可以是:\n * - 相对路径:如 `\"./pages/index.tsx\"`、`\"../components/Layout.tsx\"`\n * - 路径别名:如 `\"@/pages/index.tsx\"`(需要在构建工具中配置,如 vite.config.ts)\n * - 绝对路径:如 `\"/src/pages/index.tsx\"`(不推荐)\n * \n * 动态导入函数示例:\n * - `() => import(\"./pages/index.tsx\")`\n * - `() => import(\"@/pages/index.tsx\")`\n * \n * 路径解析由构建工具(如 Vite、Webpack)处理,框架会在运行时验证组件是否有默认导出。\n */\n routes?: RouteConfig[] | (() => RouteConfig[] | Promise<RouteConfig[]>);\n\n /**\n * 路由模式\n * \n * - `browser`: 使用 BrowserRouter(createBrowserRouter)- 默认\n * - `hash`: 使用 HashRouter(createHashRouter)\n * - `memory`: 使用 MemoryRouter(createMemoryRouter)\n * \n * @default 'browser'\n */\n mode?: RouterMode;\n\n /**\n * 路由选项\n * \n * 传递给 react-router-dom 的路由创建方法的选项\n * - `browser` 和 `hash` 模式使用 `DOMRouterOpts`\n * - `memory` 模式使用 `MemoryRouterOpts`\n */\n options?: DOMRouterOpts | MemoryRouterOpts;\n\n /**\n * 路径解析配置\n * \n * 用于配置路径解析选项,主要用于文档和错误提示。\n * 实际的路径解析由构建工具(如 Vite、Webpack)处理。\n */\n pathResolve?: PathResolveConfig;\n\n /**\n * 转换选项\n * \n * 用于配置转换选项,主要用于转换路由配置。\n */\n transformOptions?: TransformOptions;\n\n /**\n * 路由预加载配置\n *\n * 默认策略为 `none`,即按需懒加载路由组件。\n * 如需预加载,可显式配置 `strategy` 为 `next-level` / `visible` / `all`。\n */\n preload?: PreloadConfig;\n\n /**\n * 路由生命周期钩子\n */\n hooks?: RouteLifecycleHooks;\n\n /**\n * 是否启用配置验证(默认 true)\n * \n * 如果为 false,将跳过路由配置的 Zod 验证\n */\n enableValidation?: boolean;\n\n /**\n * 默认路由错误组件\n * \n * 当路由配置中的 `error`/`errors` 为空时,将使用此组件作为错误边界。\n * 如果不提供,将使用框架内置的 `RouteErrorBoundary` 组件。\n * \n * 组件必须使用默认导出(export default)。\n * \n * @example\n * ```typescript\n * router: {\n * defaultRouteErrorComponent: () => import('./components/CustomErrorBoundary'),\n * routes: [\n * {\n * name: 'home',\n * path: '/',\n * page: './pages/Home',\n * // error/errors 为空时,将使用 defaultRouteErrorComponent\n * },\n * ],\n * },\n * ```\n */\n defaultRouteErrorComponent?: ComponentImport;\n\n /**\n * 默认路由加载组件\n * \n * 当路由配置中的 `loading` 为空时,将使用此组件作为加载指示器。\n * 如果不提供,将使用框架内置 Loading 组件。\n * \n * 组件必须使用默认导出(export default)。\n * \n * @example\n * ```typescript\n * router: {\n * defaultRouteLoadingComponent: () => import('./components/CustomLoading'),\n * routes: [\n * {\n * name: 'home',\n * path: '/',\n * page: './pages/Home',\n * // loading 为空时,将使用 defaultRouteLoadingComponent\n * },\n * ],\n * },\n * ```\n */\n defaultRouteLoadingComponent?: ComponentImport;\n}\n"],"names":[],"mappings":"AA+XA;;CAEC,GACD,WA0IC"}