vitarx-router 4.0.0-beta.2 → 4.0.0-beta.20

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 (90) hide show
  1. package/README.md +8 -3
  2. package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.d.ts +1 -1
  3. package/dist/components/RouterView.js +5 -4
  4. package/dist/core/common/constant.d.ts +5 -6
  5. package/dist/core/common/constant.js +5 -6
  6. package/dist/core/common/utils.js +2 -1
  7. package/dist/core/router/checkOptions.d.ts +11 -0
  8. package/dist/core/router/checkOptions.js +119 -0
  9. package/dist/core/router/manager.js +2 -0
  10. package/dist/core/router/router.d.ts +154 -1
  11. package/dist/core/router/router.js +303 -230
  12. package/dist/core/router/web.js +6 -1
  13. package/dist/core/shared/link.d.ts +7 -0
  14. package/dist/core/shared/link.js +11 -8
  15. package/dist/core/shared/route.js +1 -2
  16. package/dist/core/types/options.d.ts +2 -0
  17. package/dist/file-router/config/index.d.ts +2 -1
  18. package/dist/file-router/config/index.js +2 -1
  19. package/dist/file-router/config/resolve.d.ts +43 -0
  20. package/dist/file-router/config/resolve.js +69 -0
  21. package/dist/file-router/{utils/validateOptions.d.ts → config/validate.d.ts} +11 -10
  22. package/dist/file-router/config/validate.js +280 -0
  23. package/dist/file-router/constants.d.ts +12 -2
  24. package/dist/file-router/constants.js +13 -3
  25. package/dist/file-router/generator/generateRoutes.d.ts +44 -13
  26. package/dist/file-router/generator/generateRoutes.js +159 -80
  27. package/dist/file-router/generator/generateTypes.d.ts +3 -29
  28. package/dist/file-router/generator/generateTypes.js +36 -41
  29. package/dist/file-router/global.d.ts +1 -1
  30. package/dist/file-router/index.d.ts +224 -90
  31. package/dist/file-router/index.js +571 -135
  32. package/dist/file-router/macros/astValueExtractor.d.ts +1 -1
  33. package/dist/file-router/macros/astValueExtractor.js +27 -7
  34. package/dist/file-router/macros/definePage.d.ts +20 -3
  35. package/dist/file-router/macros/definePage.js +120 -40
  36. package/dist/file-router/parser/exportChecker.d.ts +4 -23
  37. package/dist/file-router/parser/exportChecker.js +38 -79
  38. package/dist/file-router/parser/filterUtils.d.ts +25 -0
  39. package/dist/file-router/parser/filterUtils.js +43 -0
  40. package/dist/file-router/parser/index.d.ts +2 -1
  41. package/dist/file-router/parser/index.js +2 -1
  42. package/dist/file-router/parser/parsePage.d.ts +56 -9
  43. package/dist/file-router/parser/parsePage.js +194 -172
  44. package/dist/file-router/parser/routePath.d.ts +22 -0
  45. package/dist/file-router/parser/routePath.js +74 -0
  46. package/dist/file-router/types/hooks.d.ts +52 -0
  47. package/dist/file-router/types/index.d.ts +3 -0
  48. package/dist/file-router/types/index.js +1 -0
  49. package/dist/file-router/types/options.d.ts +279 -0
  50. package/dist/file-router/types/options.js +1 -0
  51. package/dist/file-router/types/route.d.ts +114 -0
  52. package/dist/file-router/types/route.js +1 -0
  53. package/dist/file-router/utils/fileReader.d.ts +11 -0
  54. package/dist/file-router/utils/fileReader.js +22 -0
  55. package/dist/file-router/utils/findRoute.d.ts +8 -0
  56. package/dist/file-router/utils/findRoute.js +22 -0
  57. package/dist/file-router/utils/index.d.ts +4 -2
  58. package/dist/file-router/utils/index.js +4 -2
  59. package/dist/file-router/utils/logger.d.ts +6 -6
  60. package/dist/file-router/utils/logger.js +44 -4
  61. package/dist/file-router/utils/pathStrategy.d.ts +28 -0
  62. package/dist/file-router/utils/{namingStrategy.js → pathStrategy.js} +18 -28
  63. package/dist/file-router/utils/pathUtils.d.ts +31 -0
  64. package/dist/file-router/utils/pathUtils.js +53 -1
  65. package/dist/plugin-vite/constant.d.ts +9 -0
  66. package/dist/plugin-vite/constant.js +9 -0
  67. package/dist/plugin-vite/index.d.ts +4 -24
  68. package/dist/plugin-vite/index.js +4 -94
  69. package/dist/plugin-vite/plugin.d.ts +86 -0
  70. package/dist/plugin-vite/plugin.js +181 -0
  71. package/dist/plugin-vite/watcher.d.ts +15 -0
  72. package/dist/plugin-vite/watcher.js +65 -0
  73. package/package.json +8 -6
  74. package/dist/file-router/config/configUtils.d.ts +0 -54
  75. package/dist/file-router/config/configUtils.js +0 -88
  76. package/dist/file-router/scanner/filterUtils.d.ts +0 -35
  77. package/dist/file-router/scanner/filterUtils.js +0 -188
  78. package/dist/file-router/scanner/index.d.ts +0 -8
  79. package/dist/file-router/scanner/index.js +0 -8
  80. package/dist/file-router/scanner/routeTreeBuilder.d.ts +0 -21
  81. package/dist/file-router/scanner/routeTreeBuilder.js +0 -312
  82. package/dist/file-router/scanner/scanPages.d.ts +0 -48
  83. package/dist/file-router/scanner/scanPages.js +0 -174
  84. package/dist/file-router/types.d.ts +0 -344
  85. package/dist/file-router/utils/namingStrategy.d.ts +0 -57
  86. package/dist/file-router/utils/validateOptions.js +0 -233
  87. /package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.js +0 -0
  88. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.d.ts +0 -0
  89. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.js +0 -0
  90. /package/dist/file-router/{types.js → types/hooks.js} +0 -0
package/README.md CHANGED
@@ -10,7 +10,7 @@ Vitarx 前端框架官方路由解决方案,提供声明式路由配置、导
10
10
  ## 特性
11
11
 
12
12
  - 🚀 **多种路由模式** - 支持 Hash、History、Memory 三种路由模式
13
- - 📁 **文件路由** - 基于 Vite 插件的文件系统路由自动生成
13
+ - 📁 **文件路由** - 基于 Vite 插件的文件系统路由自动生成,HMR 热更新支持
14
14
  - 🔒 **导航守卫** - 完整的路由守卫机制,支持权限控制
15
15
  - 🔄 **动态路由** - 支持动态参数、正则约束、可选参数
16
16
  - 📦 **懒加载** - 内置组件懒加载支持
@@ -403,7 +403,10 @@ import VitarxRouter from 'vitarx-router/vite'
403
403
  export default defineConfig({
404
404
  plugins: [
405
405
  VitarxRouter({
406
- pagesDir: 'src/pages'
406
+ pages: 'src/pages',
407
+ pathStrategy: 'kebab',
408
+ importMode: 'lazy',
409
+ dts: 'router.d.ts'
407
410
  })
408
411
  ]
409
412
  })
@@ -418,7 +421,9 @@ import { createRouter } from 'vitarx-router'
418
421
  export const router = createRouter({ routes })
419
422
  ```
420
423
 
421
- 详细配置请参考 [Vite 插件文档](src/plugin-vite/README.md)。
424
+ ### 文件路由配置选项
425
+
426
+ 请参考 [File Router 文档](src/file-router/README.md)。
422
427
 
423
428
  ## TypeScript 支持
424
429
 
@@ -1,4 +1,4 @@
1
- import type { Route, Router } from '../../core/index.js';
1
+ import type { Route, Router } from '../core/index.js';
2
2
  /**
3
3
  * 处理路由热更新函数
4
4
  *
@@ -12,7 +12,7 @@ import { __ROUTER_VIEW_DEPTH_KEY__, useRouter } from '../core/index.js';
12
12
  * @returns {View} 返回渲染的视图
13
13
  */
14
14
  export function RouterView(props) {
15
- const { children, name = 'default' } = props;
15
+ const { children } = props;
16
16
  // 获取路由实例
17
17
  const router = useRouter();
18
18
  // 获取父级 index
@@ -20,6 +20,7 @@ export function RouterView(props) {
20
20
  const index = parentIndex + 1; // 计算当前视图的索引
21
21
  provide(__ROUTER_VIEW_DEPTH_KEY__, index); // 向子组件提供当前索引
22
22
  // 匹配的路由线路
23
+ const viewName = computed(() => props.name || 'default');
23
24
  const matchedRoute = computed(() => {
24
25
  return router.route.matched[index] ?? null;
25
26
  });
@@ -28,8 +29,7 @@ export function RouterView(props) {
28
29
  const currentRoute = matchedRoute.value;
29
30
  if (!currentRoute)
30
31
  return null;
31
- const name = props.name || 'default'; // 获取视图名称,默认为 'default'
32
- let injectProps = currentRoute.props?.[name] ?? router.config.props ?? false;
32
+ let injectProps = currentRoute.props?.[viewName.value] ?? router.config.props ?? true;
33
33
  if (injectProps === false)
34
34
  return null; // 如果属性为 false,返回null
35
35
  if (injectProps === true && currentRoute.pattern) {
@@ -52,7 +52,7 @@ export function RouterView(props) {
52
52
  const route = matchedRoute.value; // 获取匹配的路由记录
53
53
  if (!route)
54
54
  return null;
55
- return route.component?.[name] ?? null;
55
+ return route.component?.[viewName.value] ?? null;
56
56
  });
57
57
  // 如果传入了 children 函数,则调用并返回其结果
58
58
  if (isFunction(children)) {
@@ -61,6 +61,7 @@ export function RouterView(props) {
61
61
  }
62
62
  catch (e) {
63
63
  logger.error('[RouterView] Error occurred while executing children function', e);
64
+ return createCommentView(`router-view:error`);
64
65
  }
65
66
  }
66
67
  let lastView = null;
@@ -2,12 +2,11 @@
2
2
  * 导航状态
3
3
  *
4
4
  * 枚举值:
5
- * 0. success: 导航成功
6
- * 1. aborted: 导航被阻止
7
- * 2. cancelled: 导航被取消
8
- * 3. duplicated: 重复导航
9
- * 4. notfound: 路由未匹配
10
- * 5. exception: 捕获到异常
5
+ * 1. success: 导航成功
6
+ * 2. aborted: 导航被阻止
7
+ * 4. cancelled: 导航被取消
8
+ * 8. duplicated: 重复导航
9
+ * 16. notfound: 路由未匹配
11
10
  */
12
11
  export declare enum NavState {
13
12
  /**
@@ -2,12 +2,11 @@
2
2
  * 导航状态
3
3
  *
4
4
  * 枚举值:
5
- * 0. success: 导航成功
6
- * 1. aborted: 导航被阻止
7
- * 2. cancelled: 导航被取消
8
- * 3. duplicated: 重复导航
9
- * 4. notfound: 路由未匹配
10
- * 5. exception: 捕获到异常
5
+ * 1. success: 导航成功
6
+ * 2. aborted: 导航被阻止
7
+ * 4. cancelled: 导航被取消
8
+ * 8. duplicated: 重复导航
9
+ * 16. notfound: 路由未匹配
11
10
  */
12
11
  export var NavState;
13
12
  (function (NavState) {
@@ -108,7 +108,8 @@ export function resolveNavTarget(index) {
108
108
  return {
109
109
  index: index.matched.at(-1)?.name || index.path, // 使用最后一个匹配的路由名称,如果没有则使用路径
110
110
  params: index.params, // 路由参数
111
- query: index.query // 查询参数
111
+ query: index.query, // 查询参数
112
+ hash: index.hash // 哈希值
112
113
  };
113
114
  }
114
115
  throw new Error('Invalid navigation target');
@@ -0,0 +1,11 @@
1
+ import type { RouterOptions } from '../types/index.js';
2
+ /**
3
+ * 检查路由器配置选项的合法性
4
+ *
5
+ * 在开发环境下对用户传入的 RouterOptions 进行校验,
6
+ * 确保必填字段存在、类型正确、值合法。
7
+ *
8
+ * @param options - 用户传入的路由器配置对象
9
+ * @throws {Error} 当选项不合法时抛出错误
10
+ */
11
+ export declare const checkRouterOptions: (options: RouterOptions) => void;
@@ -0,0 +1,119 @@
1
+ import { isComponent, isFunction } from 'vitarx';
2
+ import { RouteManager } from './manager.js';
3
+ /**
4
+ * 检查路由器配置选项的合法性
5
+ *
6
+ * 在开发环境下对用户传入的 RouterOptions 进行校验,
7
+ * 确保必填字段存在、类型正确、值合法。
8
+ *
9
+ * @param options - 用户传入的路由器配置对象
10
+ * @throws {Error} 当选项不合法时抛出错误
11
+ */
12
+ export const checkRouterOptions = (options) => {
13
+ // 1. 检查 options 是否为对象
14
+ if (typeof options !== 'object' || options === null) {
15
+ throw new Error('[Router] Router options must be an object.');
16
+ }
17
+ // 2. 检查 routes 是否存在且为有效类型
18
+ if (!('routes' in options) || options.routes === undefined) {
19
+ throw new Error('[Router] "routes" is a required option.');
20
+ }
21
+ if (!Array.isArray(options.routes) && !(options.routes instanceof RouteManager)) {
22
+ throw new Error('[Router] "routes" must be an array or a RouteManager instance.');
23
+ }
24
+ // 3. 检查 mode 的值是否合法
25
+ if ('mode' in options && options.mode !== undefined) {
26
+ const validModes = ['hash', 'path'];
27
+ if (!validModes.includes(options.mode)) {
28
+ throw new Error(`[Router] "mode" must be one of: ${validModes.join(', ')}. Received "${options.mode}".`);
29
+ }
30
+ }
31
+ // 4. 检查 base 的格式
32
+ if ('base' in options && options.base !== undefined) {
33
+ if (typeof options.base !== 'string') {
34
+ throw new Error('[Router] "base" must be a string.');
35
+ }
36
+ if (!options.base.startsWith('/')) {
37
+ throw new Error('[Router] "base" must start with a slash (/).');
38
+ }
39
+ }
40
+ // 5. 检查 suffix 的格式
41
+ if ('suffix' in options && options.suffix !== undefined) {
42
+ if (typeof options.suffix !== 'string') {
43
+ throw new Error('[Router] "suffix" must be a string.');
44
+ }
45
+ if (!options.suffix.startsWith('.')) {
46
+ throw new Error('[Router] "suffix" must start with a dot (.).');
47
+ }
48
+ if (options.suffix === '.') {
49
+ throw new Error('[Router] "suffix" cannot be just a dot, please provide a valid extension like ".html".');
50
+ }
51
+ }
52
+ // 6. 检查 props 的类型
53
+ if ('props' in options && options.props !== undefined) {
54
+ if (typeof options.props !== 'boolean' && !isFunction(options.props)) {
55
+ throw new Error('[Router] "props" must be a boolean or function.');
56
+ }
57
+ }
58
+ // 7. 检查 scrollBehavior 的类型
59
+ if ('scrollBehavior' in options && options.scrollBehavior !== undefined) {
60
+ if (!isFunction(options.scrollBehavior)) {
61
+ throw new Error('[Router] "scrollBehavior" must be a function.');
62
+ }
63
+ }
64
+ // 8. 检查钩子函数的类型
65
+ if ('beforeEach' in options && options.beforeEach !== undefined) {
66
+ if (!isFunction(options.beforeEach) && !Array.isArray(options.beforeEach)) {
67
+ throw new Error('[Router] "beforeEach" must be a function or an array of functions.');
68
+ }
69
+ if (Array.isArray(options.beforeEach)) {
70
+ options.beforeEach.forEach((hook, index) => {
71
+ if (!isFunction(hook)) {
72
+ throw new Error(`[Router] "beforeEach" must be a function or an array of functions. Index: ${index}`);
73
+ }
74
+ });
75
+ }
76
+ }
77
+ if ('afterEach' in options && options.afterEach !== undefined) {
78
+ if (!isFunction(options.afterEach) && !Array.isArray(options.afterEach)) {
79
+ throw new Error('[Router] "afterEach" must be a function or an array of functions.');
80
+ }
81
+ if (Array.isArray(options.afterEach)) {
82
+ options.afterEach.forEach((hook, index) => {
83
+ if (!isFunction(hook)) {
84
+ throw new Error(`[Router] "afterEach" must be a function or an array of functions. Index: ${index}`);
85
+ }
86
+ });
87
+ }
88
+ }
89
+ if ('onNotFound' in options && options.onNotFound !== undefined) {
90
+ if (!isFunction(options.onNotFound) && !Array.isArray(options.onNotFound)) {
91
+ throw new Error('[Router] "onNotFound" must be a function or an array of functions.');
92
+ }
93
+ if (Array.isArray(options.onNotFound)) {
94
+ options.onNotFound.forEach((hook, index) => {
95
+ if (!isFunction(hook)) {
96
+ throw new Error(`[Router] "onNotFound" must be a function or an array of functions. Index: ${index}`);
97
+ }
98
+ });
99
+ }
100
+ }
101
+ if ('onError' in options && options.onError !== undefined) {
102
+ if (!isFunction(options.onError) && !Array.isArray(options.onError)) {
103
+ throw new Error('[Router] "onError" must be a function or an array of functions.');
104
+ }
105
+ if (Array.isArray(options.onError)) {
106
+ options.onError.forEach((hook, index) => {
107
+ if (!isFunction(hook)) {
108
+ throw new Error(`[Router] "onError" must be a function or an array of functions. Index: ${index}`);
109
+ }
110
+ });
111
+ }
112
+ }
113
+ // 9. 检查 missing 组件的类型
114
+ if ('missing' in options && options.missing !== undefined) {
115
+ if (!isComponent(options.missing)) {
116
+ throw new Error('[Router] "missing" must be a valid component.');
117
+ }
118
+ }
119
+ };
@@ -200,6 +200,7 @@ export class RouteManager {
200
200
  const candidates = this.dynamicRoutes.get(length);
201
201
  if (candidates) {
202
202
  for (const { regex, route } of candidates) {
203
+ regex.lastIndex = 0;
203
204
  // 执行正则匹配
204
205
  const match = regex.exec(formattedPath);
205
206
  if (match) {
@@ -248,6 +249,7 @@ export class RouteManager {
248
249
  // 3. 动态路由:参数校验与序列化
249
250
  const resolvedParams = {};
250
251
  for (const paramDef of route.pattern) {
252
+ paramDef.regex.lastIndex = 0;
251
253
  const value = params[paramDef.name];
252
254
  const rawValue = String(value ?? ''); // 统一转字符串处理
253
255
  // 3.1 必填参数校验
@@ -1,6 +1,47 @@
1
1
  import { App, type ReadonlyObject } from 'vitarx';
2
2
  import type { AfterCallback, NavigateResult, NavigationGuard, NavOptions, NavTarget, ResolvedRouterConfig, Route, RouteIndex, RouteLocation, RouteRecord, RouterOptions, ScrollPosition, ScrollTarget, URLHash, URLQuery } from '../types/index.js';
3
3
  import { RouteManager } from './manager.js';
4
+ /**
5
+ * 导航上下文
6
+ *
7
+ * 在每次导航过程中创建,封装该次导航所需的所有状态和操作。
8
+ * 作为各导航阶段处理方法之间的共享数据载体,避免方法间传递大量独立参数。
9
+ *
10
+ * @internal
11
+ */
12
+ export interface NavigationContext {
13
+ /** 当前导航任务的唯一标识,用于并发导航竞争检测 */
14
+ taskId: number;
15
+ /** 导航结果对象,各阶段处理方法可修改其 state 和 message */
16
+ result: NavigateResult;
17
+ /** 目标路由位置,匹配失败时为 null */
18
+ to: RouteLocation | null;
19
+ /** 来源路由位置 */
20
+ from: RouteLocation;
21
+ /** 重定向来源路由位置,仅在重定向链中存在 */
22
+ redirectFrom: RouteLocation | undefined;
23
+ /** 是否替换当前历史记录(而非推入新记录) */
24
+ replace: boolean;
25
+ /**
26
+ * 检测重定向循环
27
+ *
28
+ * 每次重定向时调用,递增重定向计数器。
29
+ * 当重定向次数超过最大限制时抛出错误,防止无限循环。
30
+ *
31
+ * @param path - 重定向目标路径,用于错误信息
32
+ * @throws {Error} 当重定向次数超过 MAX_REDIRECTS 时
33
+ */
34
+ checkRedirectLoop: (path: string) => void;
35
+ /**
36
+ * 检测并发导航竞争
37
+ *
38
+ * 判断当前导航任务是否已被更新的导航任务取代。
39
+ * 如果已被取代,则将结果状态标记为 cancelled。
40
+ *
41
+ * @returns true 表示当前导航已被新导航取代,应中止执行
42
+ */
43
+ hasChanged: () => boolean;
44
+ }
4
45
  /**
5
46
  * 路由器抽象基类
6
47
  *
@@ -29,6 +70,11 @@ export declare abstract class Router {
29
70
  * @private
30
71
  */
31
72
  private readonly _routeLocation;
73
+ /**
74
+ * 只读路由位置对象
75
+ * @private
76
+ */
77
+ private readonly _readonlyLocation;
32
78
  /**
33
79
  * 存储就绪状态的 Promise(延迟创建)
34
80
  * @private
@@ -220,7 +266,7 @@ export declare abstract class Router {
220
266
  * await router.push('/foo')
221
267
  * await router.waitViewRender()
222
268
  *
223
- * // 此时 DOM 已确保更新
269
+ * // 此时 DOM 已更新
224
270
  * console.log(document.querySelector('#app').innerHTML)
225
271
  */
226
272
  waitViewRender(navResult?: Promise<NavigateResult>): Promise<void>;
@@ -257,8 +303,115 @@ export declare abstract class Router {
257
303
  * @returns 返回标准的导航结果 Promise
258
304
  */
259
305
  private initialNavigation;
306
+ /**
307
+ * 创建导航上下文
308
+ *
309
+ * 在每次导航开始时构建上下文对象,封装该次导航所需的所有状态:
310
+ * - 生成唯一任务ID,用于并发导航竞争检测
311
+ * - 重置重定向计数器(非重定向场景下)
312
+ * - 执行路由匹配,确定目标路由位置
313
+ * - 构建 NavigateResult 基础对象
314
+ * - 提供 checkRedirectLoop 和 hasChanged 闭包方法
315
+ *
316
+ * @param target - 导航目标
317
+ * @param fromRoute - 来源路由,默认使用当前路由位置
318
+ * @param redirectFrom - 重定向来源路由
319
+ * @returns 导航上下文对象
320
+ */
321
+ private createNavigationContext;
322
+ /**
323
+ * 处理路由未匹配(404)场景
324
+ *
325
+ * 当目标路由无法匹配时执行:
326
+ * 1. 触发全局 onNotFound 钩子
327
+ * 2. 如果钩子返回了新的导航目标,进行重定向
328
+ * 3. 否则返回 notfound 状态
329
+ *
330
+ * @param context - 导航上下文
331
+ * @param target - 原始导航目标
332
+ * @returns 导航结果
333
+ */
334
+ private handleNotFound;
335
+ /**
336
+ * 处理重复路由场景
337
+ *
338
+ * 当目标路由与当前路由的 href 和最终匹配记录完全相同时,
339
+ * 视为重复导航,返回 duplicated 状态,不执行后续流程。
340
+ *
341
+ * @param context - 导航上下文
342
+ * @returns 重复路由结果,或 null 表示非重复路由
343
+ */
344
+ private handleDuplicatedRoute;
345
+ /**
346
+ * 处理仅 hash 变化场景
347
+ *
348
+ * 当路由路径和查询参数相同,仅 hash 部分发生变化时,
349
+ * 直接更新 hash 值并触发 hashUpdate 回调,不执行完整的导航流程。
350
+ *
351
+ * @param context - 导航上下文
352
+ * @returns hash 变化结果,或 null 表示非仅 hash 变化
353
+ */
354
+ private handleHashOnlyChange;
355
+ /**
356
+ * 处理路由重定向场景
357
+ *
358
+ * 当目标路由配置了 redirect 字段时,解析重定向目标并递归执行导航:
359
+ * 1. 支持 redirect 为函数(动态重定向)或静态值
360
+ * 2. 重定向目标可以是路由索引(RouteIndex)或导航目标(NavTarget)
361
+ * 3. 如果重定向配置无效且无组件定义,抛出错误
362
+ *
363
+ * @param context - 导航上下文
364
+ * @returns 重定向导航结果,或 null 表示无需重定向
365
+ */
366
+ private handleRedirect;
367
+ /**
368
+ * 执行守卫流程
369
+ *
370
+ * 按顺序执行路由离开守卫和全局前置守卫,
371
+ * 在每个异步守卫执行后进行并发竞争检测。
372
+ * 如果守卫拦截导航或触发重定向,返回对应结果;
373
+ * 如果守卫全部通过,返回 null 表示继续导航。
374
+ *
375
+ * @param context - 导航上下文
376
+ * @returns 守卫拦截/重定向结果,或 null 表示守卫全部通过
377
+ */
378
+ private executeGuards;
379
+ /**
380
+ * 处理前置守卫执行结果
381
+ *
382
+ * 根据守卫返回值决定导航走向:
383
+ * - false: 拦截导航,返回 aborted 状态
384
+ * - NavTarget: 重定向到新目标
385
+ * - 其他(true/void): 放行,继续导航
386
+ *
387
+ * @param context - 导航上下文
388
+ * @param guardResult - 前置守卫的返回值
389
+ * @returns 拦截/重定向结果,或 null 表示守卫通过
390
+ */
391
+ private handleGuardResult;
392
+ /**
393
+ * 完成导航流程
394
+ *
395
+ * 守卫全部通过后执行最后的导航确认:
396
+ * 1. 根据替换标记更新历史记录(push 或 replace)
397
+ * 2. 调用 completeNavigation 更新路由状态、触发后置钩子和滚动行为
398
+ *
399
+ * @param context - 导航上下文
400
+ * @returns 导航成功结果
401
+ */
402
+ private finalizeNavigation;
260
403
  /**
261
404
  * 导航到指定位置
405
+ *
406
+ * 作为导航流程的编排器,按顺序协调各场景处理方法的执行:
407
+ * 1. 创建导航上下文(路由匹配、并发控制初始化)
408
+ * 2. 处理 404 场景(路由未匹配)
409
+ * 3. 处理重复路由场景
410
+ * 4. 处理仅 hash 变化场景
411
+ * 5. 处理路由重定向场景
412
+ * 6. 执行守卫流程(离开守卫 → 前置守卫)
413
+ * 7. 完成导航(更新历史记录、触发后置钩子)
414
+ *
262
415
  * @param target - 导航目标对象 | 路由位置对象
263
416
  * @param fromRoute - 来源路由对象
264
417
  * @param redirectFrom - 重定向来源对象