vitarx-router 4.0.0-beta.9 → 4.0.1

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 (73) hide show
  1. package/README.md +125 -40
  2. package/dist/auto-routes/index.d.ts +26 -7
  3. package/dist/auto-routes/index.js +78 -2
  4. package/dist/components/RouterLink.d.ts +10 -5
  5. package/dist/components/RouterLink.js +37 -17
  6. package/dist/components/RouterView.d.ts +4 -3
  7. package/dist/components/RouterView.js +6 -4
  8. package/dist/core/common/constant.d.ts +12 -26
  9. package/dist/core/common/constant.js +8 -22
  10. package/dist/core/common/utils.d.ts +20 -3
  11. package/dist/core/common/utils.js +31 -11
  12. package/dist/core/common/variable.js +1 -1
  13. package/dist/core/index.d.ts +1 -0
  14. package/dist/core/index.js +1 -0
  15. package/dist/core/router/checkOptions.js +1 -7
  16. package/dist/core/router/manager.d.ts +4 -14
  17. package/dist/core/router/manager.js +36 -42
  18. package/dist/core/router/router.d.ts +43 -15
  19. package/dist/core/router/router.js +123 -60
  20. package/dist/core/router/web.d.ts +13 -0
  21. package/dist/core/router/web.js +43 -7
  22. package/dist/core/shared/link.d.ts +48 -4
  23. package/dist/core/shared/link.js +92 -47
  24. package/dist/core/shared/router.d.ts +5 -5
  25. package/dist/core/shared/router.js +7 -5
  26. package/dist/core/shared/utils.d.ts +41 -5
  27. package/dist/core/shared/utils.js +64 -8
  28. package/dist/core/types/hooks.d.ts +13 -4
  29. package/dist/core/types/navigation.d.ts +1 -1
  30. package/dist/core/types/options.d.ts +3 -10
  31. package/dist/file-router/config/resolve.d.ts +4 -2
  32. package/dist/file-router/config/resolve.js +4 -2
  33. package/dist/file-router/config/validate.js +28 -2
  34. package/dist/file-router/generator/generateRoutes.d.ts +11 -4
  35. package/dist/file-router/generator/generateRoutes.js +34 -10
  36. package/dist/file-router/index.d.ts +11 -186
  37. package/dist/file-router/index.js +14 -523
  38. package/dist/file-router/macros/astValueExtractor.js +32 -8
  39. package/dist/file-router/macros/definePage.d.ts +3 -1
  40. package/dist/file-router/macros/definePage.js +6 -4
  41. package/dist/file-router/manager/file-classifier.d.ts +68 -0
  42. package/dist/file-router/manager/file-classifier.js +63 -0
  43. package/dist/file-router/manager/file-processor.d.ts +120 -0
  44. package/dist/file-router/manager/file-processor.js +130 -0
  45. package/dist/file-router/manager/index.d.ts +136 -0
  46. package/dist/file-router/manager/index.js +253 -0
  47. package/dist/file-router/manager/route-updater.d.ts +80 -0
  48. package/dist/file-router/manager/route-updater.js +225 -0
  49. package/dist/file-router/manager/scanner.d.ts +13 -0
  50. package/dist/file-router/manager/scanner.js +143 -0
  51. package/dist/file-router/parser/filterUtils.js +1 -2
  52. package/dist/file-router/parser/parsePage.d.ts +30 -6
  53. package/dist/file-router/parser/parsePage.js +72 -59
  54. package/dist/file-router/parser/routePath.d.ts +22 -0
  55. package/dist/file-router/parser/routePath.js +74 -0
  56. package/dist/file-router/types/hooks.d.ts +37 -3
  57. package/dist/file-router/types/options.d.ts +73 -17
  58. package/dist/file-router/types/route.d.ts +20 -8
  59. package/dist/file-router/utils/findRoute.d.ts +8 -0
  60. package/dist/file-router/utils/findRoute.js +22 -0
  61. package/dist/file-router/utils/index.d.ts +1 -0
  62. package/dist/file-router/utils/index.js +1 -0
  63. package/dist/file-router/utils/logger.d.ts +4 -4
  64. package/dist/file-router/utils/logger.js +41 -1
  65. package/dist/plugin-vite/index.d.ts +3 -2
  66. package/dist/plugin-vite/index.js +3 -2
  67. package/dist/plugin-vite/plugin.d.ts +1 -2
  68. package/dist/plugin-vite/plugin.js +8 -23
  69. package/dist/plugin-vite/{watcher.d.ts → utils.d.ts} +17 -0
  70. package/dist/plugin-vite/{watcher.js → utils.js} +40 -0
  71. package/package.json +13 -7
  72. package/dist/auto-routes/handleHotUpdate.d.ts +0 -34
  73. package/dist/auto-routes/handleHotUpdate.js +0 -58
package/README.md CHANGED
@@ -112,6 +112,25 @@ import { createMemoryRouter } from 'vitarx-router'
112
112
  createMemoryRouter({ routes })
113
113
  ```
114
114
 
115
+ ### 手动初始化
116
+
117
+ 默认情况下,`createRouter` / `createWebRouter` 会在创建实例后自动初始化路由器(执行初始导航并注册浏览器事件监听)。如果需要延迟初始化,可以使用 `createWebRouter` 并将第二个参数 `autoInit` 设为 `false`,然后手动调用 `init()` 方法。
118
+
119
+ ```typescript
120
+ const router = createWebRouter({ routes }, false)
121
+
122
+ // 在合适的时机手动初始化
123
+ router.init()
124
+ ```
125
+
126
+ **适用场景:**
127
+
128
+ - 需要在初始化前注册导航守卫
129
+ - 需要在初始化前完成异步配置加载
130
+ - 需要精确控制初始化时机
131
+
132
+ > **注意:** `autoInit` 仅对 Web 路由器有效。
133
+
115
134
  ## 路由配置
116
135
 
117
136
  ### 基本路由
@@ -272,6 +291,71 @@ if (hasSuccess(result)) {
272
291
  | `cancelled` | 4 | 导航被新导航取消 |
273
292
  | `duplicated` | 8 | 重复导航 |
274
293
  | `notfound` | 16 | 路由未匹配 |
294
+ | `external` | 32 | 外部跳转 |
295
+
296
+ ## 路由未匹配处理
297
+
298
+ 当路由匹配失败(404)时,可以通过 `onNotFound` 钩子进行自定义处理。
299
+
300
+ ### onNotFound 钩子
301
+
302
+ ```typescript
303
+ import { createRouter } from 'vitarx-router'
304
+
305
+ const router = createRouter({
306
+ routes: [],
307
+ onNotFound(target) {
308
+ // target.index 为用户尝试访问的目标
309
+ console.log('路由未匹配:', target.index)
310
+ }
311
+ })
312
+ ```
313
+
314
+ **返回值说明:**
315
+
316
+ | 返回值 | 说明 |
317
+ |----------------------------|----------------------|
318
+ | `NavTarget` / `RouteIndex` | 重定向到新目标 |
319
+ | `RouteLocation` | 作为未匹配路由的位置对象(渲染指定组件) |
320
+ | `void` | 不处理,返回 `notfound` 状态 |
321
+
322
+ ### 重定向到 404 页面
323
+
324
+ ```typescript
325
+ import { createRouter } from 'vitarx-router'
326
+
327
+ const router = createRouter({
328
+ routes: [],
329
+ onNotFound(target) {
330
+ return { index: '/404' }
331
+ }
332
+ })
333
+ ```
334
+
335
+ ### 渲染 404 组件(使用 createMissingRoute)
336
+
337
+ ```typescript
338
+ import { createRouter, createMissingRoute } from 'vitarx-router'
339
+ import NotFoundPage from './pages/NotFound.jsx'
340
+
341
+ const router = createRouter({
342
+ routes: [],
343
+ onNotFound(target) {
344
+ return createMissingRoute(NotFoundPage, target, {
345
+ title: '页面未找到'
346
+ })
347
+ }
348
+ })
349
+ ```
350
+
351
+ ### 名称导航不匹配
352
+
353
+ 名称导航(name-based)匹配失败时,路由器会直接抛出错误,因为名称导航是编程式调用,name 不存在属于代码 bug:
354
+
355
+ ```typescript
356
+ // 如果 'userDetail' 路由不存在,将抛出错误
357
+ router.push({ index: 'userDetail', params: { id: '123' } })
358
+ ```
275
359
 
276
360
  ## 导航守卫
277
361
 
@@ -423,20 +507,7 @@ export const router = createRouter({ routes })
423
507
 
424
508
  ### 文件路由配置选项
425
509
 
426
- | 选项 | 类型 | 默认值 | 说明 |
427
- |------------------|--------------------------------------------|---------------|----------|
428
- | `pages` | `PageSource \| readonly PageSource[]` | `'src/pages'` | 页面来源配置 |
429
- | `pathStrategy` | `'kebab' \| 'lowercase' \| 'raw'` | `'kebab'` | 路径格式化策略 |
430
- | `importMode` | `'lazy' \| 'sync' \| ImportModeFunction` | `'lazy'` | 组件导入模式 |
431
- | `injectImports` | `readonly string[]` | - | 自定义导入语句 |
432
- | `dts` | `boolean \| string` | `false` | 类型声明文件配置 |
433
- | `layoutFileName` | `string` | `'_layout'` | 布局文件名 |
434
- | `configFileName` | `string` | `'_config'` | 分组配置文件名 |
435
- | `transform` | `CodeTransformHook` | - | 代码转换钩子 |
436
- | `extendRoute` | `ExtendRouteHook` | - | 路由扩展钩子 |
437
- | `pathParser` | `PathParser` | - | 自定义路径解析器 |
438
-
439
- 详细配置请参考 [File Router 文档](src/file-router/README.md)。
510
+ 请参考 [File Router 文档](src/file-router/README.md)。
440
511
 
441
512
  ## TypeScript 支持
442
513
 
@@ -466,35 +537,49 @@ declare module 'vitarx-router' {
466
537
 
467
538
  ### 助手函数
468
539
 
469
- | 函数 | 说明 |
470
- |----------------------------------------|-----------------|
471
- | `createRouter(options)` | 创建路由器实例 |
472
- | `createWebRouter(options)` | 创建 Web 模式路由器 |
473
- | `createMemoryRouter(options)` | 创建 Memory 模式路由器 |
474
- | `createRouteManager(routes, options?)` | 创建路由管理器 |
475
- | `defineRoutes(...routes)` | 定义路由表 |
476
- | `useRouter()` | 获取路由器实例 |
477
- | `useRoute(global?)` | 获取当前路由信息 |
478
- | `useLink(options)` | 创建链接助手 |
479
- | `onBeforeRouteLeave(guard)` | 注册离开守卫 |
480
- | `onBeforeRouteUpdate(callback)` | 注册更新钩子 |
540
+ | 函数 | 说明 |
541
+ |------------------------------------------------|---------------------------|
542
+ | `createRouter(options)` | 创建路由器实例 |
543
+ | `createWebRouter(options)` | 创建 Web 模式路由器 |
544
+ | `createMemoryRouter(options)` | 创建 Memory 模式路由器 |
545
+ | `createRouteManager(routes, options?)` | 创建路由管理器 |
546
+ | `defineRoutes(...routes)` | 定义路由表 |
547
+ | `createMissingRoute(component, target, meta?)` | 创建未匹配路由的 RouteLocation |
548
+ | `cloneRouteLocation(location)` | 克隆 RouteLocation |
549
+ | `useRouter()` | 获取路由器实例 |
550
+ | `useRoute(global?)` | 获取当前路由信息 |
551
+ | `useLink(options)` | 创建链接助手 |
552
+ | `onBeforeRouteLeave(guard)` | 注册离开守卫 |
553
+ | `onBeforeRouteUpdate(callback)` | 注册更新钩子 |
554
+ | `removeTrailingSlash(path)` | 删除路径末尾的斜杠 |
555
+ | `normalizePath(path, removeTrailingSlash?)` | 规范化路径 |
556
+ | `parseQuery(queryString)` | 解析查询字符串 |
557
+ | `stringifyQuery(query)` | 序列化查询对象 |
558
+ | `isNavTarget(value)` | 检查一个值是否为导航目标对象 |
559
+ | `isNavIndex(value)` | 检查一个值是否为有效的导航索引 |
560
+ | `isRouteLocation(value)` | 检查一个值是否为 RouteLocation 对象 |
561
+ | `isRoutePath(index)` | 检查一个值是否为有效的路由路径 |
562
+ | `isExternalLink(href)` | 检查一个值是否为外部链接 |
563
+ | `isPathExactMatch(currentPath, targetPath)` | 判断路径是否完全匹配 |
564
+ | `isPathPrefixMatch(currentPath, targetPath)` | 判断路径是否前缀匹配 |
481
565
 
482
566
  ### Router 实例方法
483
567
 
484
- | 方法 | 说明 |
485
- |----------------------------|-----------|
486
- | `push(target)` | 跳转到新路由 |
487
- | `replace(target)` | 替换当前路由 |
488
- | `go(delta)` | 前进/后退指定步数 |
489
- | `back()` | 后退一步 |
490
- | `forward()` | 前进一步 |
491
- | `addRoute(route, parent?)` | 添加路由 |
492
- | `removeRoute(index)` | 移除路由 |
493
- | `hasRoute(index)` | 检查路由是否存在 |
494
- | `matchRoute(target)` | 匹配路由 |
495
- | `beforeEach(guard)` | 添加前置守卫 |
496
- | `afterEach(callback)` | 添加后置回调 |
497
- | `destroy()` | 销毁路由器 |
568
+ | 方法 | 说明 |
569
+ |----------------------------|-----------------------|
570
+ | `init()` | 手动初始化路由器(仅 WebRouter) |
571
+ | `push(target)` | 跳转到新路由 |
572
+ | `replace(target)` | 替换当前路由 |
573
+ | `go(delta)` | 前进/后退指定步数 |
574
+ | `back()` | 后退一步 |
575
+ | `forward()` | 前进一步 |
576
+ | `addRoute(route, parent?)` | 添加路由 |
577
+ | `removeRoute(index)` | 移除路由 |
578
+ | `hasRoute(index)` | 检查路由是否存在 |
579
+ | `matchRoute(target)` | 匹配路由 |
580
+ | `beforeEach(guard)` | 添加前置守卫 |
581
+ | `afterEach(callback)` | 添加后置回调 |
582
+ | `destroy()` | 销毁路由器 |
498
583
 
499
584
  ### Router 实例属性
500
585
 
@@ -1,19 +1,38 @@
1
+ import type { Route, Router } from '../core/index.js';
1
2
  /**
2
- * @fileoverview 自动路由模块
3
+ * 自动生成的路线配置
4
+ */
5
+ export declare const routes: Route[];
6
+ /**
7
+ * 处理路由热更新函数
8
+ *
9
+ * 当路由配置发生变化时,动态更新路由表。
10
+ * 支持传入回调函数,在路由更新后执行自定义逻辑(如添加重定向)。
3
11
  *
4
- * 该模块重新导出虚拟模块生成的路由配置。
5
- * 开发者应该从 `vitarx-router/auto-routes` 导入 routes。
12
+ * @param router - Vitarx Router 实例
13
+ * @param onRoutesUpdated - 路由更新后的回调函数,接收新路由数组作为参数
6
14
  *
7
15
  * @example
8
- * ```typescript
16
+ * ```TypeScript
17
+ * import { createRouter } from 'vitarx'
9
18
  * import { routes, handleHotUpdate } from 'vitarx-router/auto-routes'
10
19
  *
11
20
  * const router = createRouter({ routes })
12
21
  *
22
+ * function addRedirects() {
23
+ * router.addRoute({
24
+ * path: '/new-about',
25
+ * redirect: '/about',
26
+ * })
27
+ * }
28
+ *
13
29
  * if (import.meta.hot) {
14
- * handleHotUpdate(router)
30
+ * handleHotUpdate(router, (newRoutes) => {
31
+ * addRedirects()
32
+ * })
33
+ * } else {
34
+ * addRedirects()
15
35
  * }
16
36
  * ```
17
37
  */
18
- export { default as routes } from 'virtual:vitarx-router:routes';
19
- export { handleHotUpdate } from './handleHotUpdate.js';
38
+ export declare function handleHotUpdate(router: Router, onRoutesUpdated?: (newRoutes: Route[]) => void): void;
@@ -15,5 +15,81 @@
15
15
  * }
16
16
  * ```
17
17
  */
18
- export { default as routes } from 'virtual:vitarx-router:routes';
19
- export { handleHotUpdate } from './handleHotUpdate.js';
18
+ import virtualRoutes from 'virtual:vitarx-router:routes';
19
+ /**
20
+ * 自动生成的路线配置
21
+ */
22
+ export const routes = virtualRoutes;
23
+ /**
24
+ * 处理路由热更新函数
25
+ *
26
+ * 当路由配置发生变化时,动态更新路由表。
27
+ * 支持传入回调函数,在路由更新后执行自定义逻辑(如添加重定向)。
28
+ *
29
+ * @param router - Vitarx Router 实例
30
+ * @param onRoutesUpdated - 路由更新后的回调函数,接收新路由数组作为参数
31
+ *
32
+ * @example
33
+ * ```TypeScript
34
+ * import { createRouter } from 'vitarx'
35
+ * import { routes, handleHotUpdate } from 'vitarx-router/auto-routes'
36
+ *
37
+ * const router = createRouter({ routes })
38
+ *
39
+ * function addRedirects() {
40
+ * router.addRoute({
41
+ * path: '/new-about',
42
+ * redirect: '/about',
43
+ * })
44
+ * }
45
+ *
46
+ * if (import.meta.hot) {
47
+ * handleHotUpdate(router, (newRoutes) => {
48
+ * addRedirects()
49
+ * })
50
+ * } else {
51
+ * addRedirects()
52
+ * }
53
+ * ```
54
+ */
55
+ export function handleHotUpdate(router, onRoutesUpdated) {
56
+ if (import.meta.hot) {
57
+ import.meta.hot.accept('virtual:vitarx-router:routes', newModule => {
58
+ if (!newModule)
59
+ return;
60
+ const routes = newModule.default;
61
+ if (routes && typeof routes === 'object') {
62
+ router.manager.clearRoutes();
63
+ for (const route of routes) {
64
+ router.manager.addRoute(route);
65
+ }
66
+ // 路由更新完成后调用回调函数
67
+ if (onRoutesUpdated) {
68
+ onRoutesUpdated(routes);
69
+ }
70
+ if (typeof window === 'undefined')
71
+ return;
72
+ const matchedRoute = router.matchRoute({
73
+ index: router.route.matched.at(-1)?.name || router.route.path,
74
+ params: { ...router.route.params },
75
+ query: { ...router.route.query },
76
+ hash: router.route.hash
77
+ });
78
+ // 如果没有匹配到路由,则重新加载页面
79
+ if (!matchedRoute) {
80
+ return window.location.reload();
81
+ }
82
+ // 热更新meta信息
83
+ const meta = router['_routeLocation'].meta;
84
+ for (const key in meta) {
85
+ if (!(key in matchedRoute.meta)) {
86
+ delete meta[key];
87
+ }
88
+ }
89
+ for (const key in matchedRoute.meta) {
90
+ meta[key] = matchedRoute.meta[key];
91
+ }
92
+ }
93
+ });
94
+ }
95
+ }
@@ -1,11 +1,11 @@
1
- import { ElementView, type ValidChildren, type WithProps } from 'vitarx';
1
+ import { type CodeLocation, ElementView, type RenderChildren, type WithProps } from 'vitarx';
2
2
  import { type NavigateResult } from '../core/index.js';
3
3
  import { type UseLinkOptions } from '../core/shared/index.js';
4
4
  export interface RouterLinkProps extends UseLinkOptions, WithProps<'a'> {
5
5
  /**
6
6
  * 子节点插槽
7
7
  */
8
- children?: ValidChildren;
8
+ children?: RenderChildren;
9
9
  /**
10
10
  * 是否禁用
11
11
  *
@@ -26,10 +26,12 @@ export interface RouterLinkProps extends UseLinkOptions, WithProps<'a'> {
26
26
  * 当链接完全匹配时应用的类名
27
27
  */
28
28
  exactActiveClass?: string;
29
+ /**
30
+ * 当链接禁用时应用的类名
31
+ */
32
+ disabledClass?: string;
29
33
  /**
30
34
  * Value passed to the attribute `aria-current` when the link is exact active.
31
- *
32
- * @defaultValue `'page'`
33
35
  */
34
36
  ariaCurrentValue?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false';
35
37
  }
@@ -40,6 +42,7 @@ export interface RouterLinkProps extends UseLinkOptions, WithProps<'a'> {
40
42
  * 动态设置激活状态类名、`href` 属性以及 `aria-current` 无障碍属性。
41
43
  *
42
44
  * @param {RouterLinkProps} props - 组件属性
45
+ * @param {CodeLocation} [location] - 代码位置信息
43
46
  * @returns {ElementView<'a'>} 返回一个锚点元素视图
44
47
  *
45
48
  * @example
@@ -54,6 +57,8 @@ export interface RouterLinkProps extends UseLinkOptions, WithProps<'a'> {
54
57
  * <RouterLink to="/about" disabled>关于我们</RouterLink>
55
58
  * // 透传属性
56
59
  * <RouterLink to="/about" class="nav-link">关于我们</RouterLink>
60
+ * // 带参数的导航
61
+ * <RouterLink to={{ index: '/user', query: { id: 123 } }}>用户信息</RouterLink>
57
62
  * ```
58
63
  */
59
- export declare function RouterLink(props: RouterLinkProps): ElementView<'a'>;
64
+ export declare function RouterLink(props: RouterLinkProps, location?: CodeLocation): ElementView<'a'>;
@@ -1,5 +1,19 @@
1
- import { createView, isFunction } from 'vitarx';
1
+ import { createView, isFunction, isPlainObject, logger } from 'vitarx';
2
+ import { isExternalLink } from '../core/index.js';
2
3
  import { useLink } from '../core/shared/index.js';
4
+ const EXTRA_PROPS = [
5
+ 'to',
6
+ 'replace',
7
+ 'viewTransition',
8
+ 'exactMatchMode',
9
+ 'disabled',
10
+ 'callback',
11
+ 'onclick',
12
+ 'activeClass',
13
+ 'exactActiveClass',
14
+ 'disabledClass',
15
+ 'ariaCurrentValue'
16
+ ];
3
17
  /**
4
18
  * 路由链接组件,渲染为一个 `<a>` 标签,用于在应用内进行声明式导航。
5
19
  *
@@ -7,6 +21,7 @@ import { useLink } from '../core/shared/index.js';
7
21
  * 动态设置激活状态类名、`href` 属性以及 `aria-current` 无障碍属性。
8
22
  *
9
23
  * @param {RouterLinkProps} props - 组件属性
24
+ * @param {CodeLocation} [location] - 代码位置信息
10
25
  * @returns {ElementView<'a'>} 返回一个锚点元素视图
11
26
  *
12
27
  * @example
@@ -21,10 +36,17 @@ import { useLink } from '../core/shared/index.js';
21
36
  * <RouterLink to="/about" disabled>关于我们</RouterLink>
22
37
  * // 透传属性
23
38
  * <RouterLink to="/about" class="nav-link">关于我们</RouterLink>
39
+ * // 带参数的导航
40
+ * <RouterLink to={{ index: '/user', query: { id: 123 } }}>用户信息</RouterLink>
24
41
  * ```
25
42
  */
26
- export function RouterLink(props) {
43
+ export function RouterLink(props, location) {
27
44
  const link = useLink(props);
45
+ if (__VITARX_DEV__) {
46
+ if (!link.route.value && !isExternalLink(link.href.value)) {
47
+ logger.warn(`[RouterLink] No match found for to: ${isPlainObject(props.to) ? JSON.stringify(props.to) : String(props.to)}`, location);
48
+ }
49
+ }
28
50
  const isDisabled = () => props.disabled ?? false;
29
51
  const navigate = async (e) => {
30
52
  if (isDisabled())
@@ -36,28 +58,26 @@ export function RouterLink(props) {
36
58
  const aProps = {
37
59
  onClick: navigate,
38
60
  children: props.children,
39
- 'v-bind': [
40
- props,
41
- ['to', 'children', 'href', 'disabled', 'callback', 'onClick', 'onclick', 'aria-current']
42
- ],
61
+ 'v-bind': [props, EXTRA_PROPS],
43
62
  get class() {
44
63
  return [
45
- !isDisabled() && link.isActive.value ? props.activeClass : undefined,
46
- !isDisabled() && link.isExactActive.value ? props.exactActiveClass : undefined
64
+ props.activeClass && link.isActive.value ? props.activeClass : undefined,
65
+ props.exactActiveClass && link.isExactActive.value ? props.exactActiveClass : undefined,
66
+ props.disabledClass && isDisabled() ? props.disabledClass : undefined
47
67
  ].filter(Boolean);
48
68
  },
49
69
  get href() {
50
70
  return link.href.value;
51
- },
52
- get draggable() {
53
- return props.draggable ?? false;
54
- },
55
- get 'aria-current'() {
56
- return link.isActive.value && !isDisabled() ? props.ariaCurrentValue || 'page' : undefined;
57
- },
58
- get disabled() {
59
- return isDisabled() ? '' : undefined;
60
71
  }
61
72
  };
73
+ if ('ariaCurrentValue' in props) {
74
+ Object.defineProperty(aProps, 'aria-current', {
75
+ enumerable: true,
76
+ configurable: true,
77
+ get() {
78
+ return link.isExactActive.value ? props.ariaCurrentValue : undefined;
79
+ }
80
+ });
81
+ }
62
82
  return createView('a', aProps);
63
83
  }
@@ -8,17 +8,18 @@ export interface RouterViewOptions {
8
8
  */
9
9
  name?: string;
10
10
  /**
11
- * 渲染页面组件
11
+ * 自定义渲染当前路由匹配到的组件
12
12
  *
13
- * 接收两个参数:
13
+ * 接收三个参数:
14
14
  * - `component: Computed<Component | null>`:当前要渲染的组件,
15
15
  * - `props: Computed<AnyProps | null>`:要注入给组件的属性对象
16
+ * - `route: Computed<RouteRecord | null>`:当前匹配的路由记录
16
17
  *
17
18
  * @example
18
19
  * ```jsx
19
20
  * // 搭配 Freeze 使用
20
21
  * <RouterView>
21
- * {(component, props, path) => <Freeze is={component} props={props}/>}
22
+ * {(component, props) => <Freeze is={component} props={props}/>}
22
23
  * </RouterView>
23
24
  * ```
24
25
  *
@@ -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,8 @@ 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');
24
+ // 匹配的路由记录
23
25
  const matchedRoute = computed(() => {
24
26
  return router.route.matched[index] ?? null;
25
27
  });
@@ -28,8 +30,7 @@ export function RouterView(props) {
28
30
  const currentRoute = matchedRoute.value;
29
31
  if (!currentRoute)
30
32
  return null;
31
- const name = props.name || 'default'; // 获取视图名称,默认为 'default'
32
- let injectProps = currentRoute.props?.[name] ?? router.config.props ?? false;
33
+ let injectProps = currentRoute.props?.[viewName.value] ?? router.config.props ?? true;
33
34
  if (injectProps === false)
34
35
  return null; // 如果属性为 false,返回null
35
36
  if (injectProps === true && currentRoute.pattern) {
@@ -52,7 +53,7 @@ export function RouterView(props) {
52
53
  const route = matchedRoute.value; // 获取匹配的路由记录
53
54
  if (!route)
54
55
  return null;
55
- return route.component?.[name] ?? null;
56
+ return route.component?.[viewName.value] ?? null;
56
57
  });
57
58
  // 如果传入了 children 函数,则调用并返回其结果
58
59
  if (isFunction(children)) {
@@ -61,6 +62,7 @@ export function RouterView(props) {
61
62
  }
62
63
  catch (e) {
63
64
  logger.error('[RouterView] Error occurred while executing children function', e);
65
+ return createCommentView(`router-view:error`);
64
66
  }
65
67
  }
66
68
  let lastView = null;
@@ -2,34 +2,20 @@
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: 路由未匹配
10
+ * 32. external: 外部链接(不由路由器处理,useLink navigate 内使用)
11
11
  */
12
12
  export declare enum NavState {
13
- /**
14
- * 导航成功 (二进制: 0001)
15
- */
16
- success = 1,// 1
17
- /**
18
- * 导航被阻止 (二进制: 0010)
19
- */
20
- aborted = 2,// 2
21
- /**
22
- * 导航被取消 (二进制: 0100)
23
- */
24
- cancelled = 4,// 4
25
- /**
26
- * 重复导航 (二进制: 1000)
27
- */
28
- duplicated = 8,// 8
29
- /**
30
- * 路由未匹配 (二进制: 10000)
31
- */
32
- notfound = 16
13
+ success = 1,// 1 - 导航成功
14
+ aborted = 2,// 2 - 导航被阻止
15
+ cancelled = 4,// 4 - 导航被取消
16
+ duplicated = 8,// 8 - 重复导航
17
+ notfound = 16,// 16 - 路由未匹配
18
+ external = 32
33
19
  }
34
20
  /**
35
21
  * 路由器注入键
@@ -2,35 +2,21 @@
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: 路由未匹配
10
+ * 32. external: 外部链接(不由路由器处理,useLink navigate 内使用)
11
11
  */
12
12
  export var NavState;
13
13
  (function (NavState) {
14
- /**
15
- * 导航成功 (二进制: 0001)
16
- */
17
14
  NavState[NavState["success"] = 1] = "success";
18
- /**
19
- * 导航被阻止 (二进制: 0010)
20
- */
21
15
  NavState[NavState["aborted"] = 2] = "aborted";
22
- /**
23
- * 导航被取消 (二进制: 0100)
24
- */
25
16
  NavState[NavState["cancelled"] = 4] = "cancelled";
26
- /**
27
- * 重复导航 (二进制: 1000)
28
- */
29
17
  NavState[NavState["duplicated"] = 8] = "duplicated";
30
- /**
31
- * 路由未匹配 (二进制: 10000)
32
- */
33
- NavState[NavState["notfound"] = 16] = "notfound"; // 16
18
+ NavState[NavState["notfound"] = 16] = "notfound";
19
+ NavState[NavState["external"] = 32] = "external"; // 32 - 外部链接(不由路由器处理)
34
20
  })(NavState || (NavState = {}));
35
21
  /**
36
22
  * 路由器注入键
@@ -1,12 +1,29 @@
1
1
  import { type AnyCallback } from 'vitarx';
2
2
  import type { GuardResult, NavTarget, RouteIndex, RouteLocation, RoutePath, URLHash, URLQuery } from '../types/index.js';
3
+ /**
4
+ * 判断是否为外部链接
5
+ *
6
+ * @param href - 链接地址
7
+ * @returns 是否为外部链接
8
+ */
9
+ export declare function isExternalLink(href: string): boolean;
3
10
  /**
4
11
  * 检查给定的值是否为一个合法的导航配置对象
5
12
  *
6
13
  * @param val 要检查的未知类型值
7
14
  * @returns {boolean} 如果值是一个导航目标对象则返回true,否则返回false
8
15
  */
9
- export declare function hasValidNavTarget(val: unknown): val is NavTarget;
16
+ export declare function isNavTarget(val: unknown): val is NavTarget;
17
+ /**
18
+ * 检查给定的值是否为 RouteLocation 对象
19
+ *
20
+ * 通过检测 `matched` 和 `path` 属性来区分 RouteLocation 与 NavTarget,
21
+ * 因为 NavTarget 不包含这两个字段,而 RouteLocation 必定包含。
22
+ *
23
+ * @param val - 需要检查的值
24
+ * @returns {val is RouteLocation} 如果是 RouteLocation 对象则返回 true
25
+ */
26
+ export declare function isRouteLocation(val: unknown): val is RouteLocation;
10
27
  /**
11
28
  * 检查给定的值是否为有效的路由索引
12
29
  *
@@ -14,7 +31,7 @@ export declare function hasValidNavTarget(val: unknown): val is NavTarget;
14
31
  * @returns {boolean} 返回一个布尔值,表示值是否为有效的路由索引
15
32
  * 同时使用类型谓词(val is RouteIndex)来缩小类型范围
16
33
  */
17
- export declare function hasValidRouteIndex(val: unknown): val is RouteIndex;
34
+ export declare function isNavIndex(val: unknown): val is RouteIndex;
18
35
  /**
19
36
  * 判断两个路由位置对象是否只有 hash 不同
20
37
  *
@@ -28,7 +45,7 @@ export declare function hasOnlyChangeHash(route1: RouteLocation, route2: RouteLo
28
45
  * @param index - 要判断的索引
29
46
  * @returns {boolean} - 如果索引为路径索引则返回true,否则返回false
30
47
  */
31
- export declare function hasValidPath(index: any): index is RoutePath;
48
+ export declare function isRoutePath(index: unknown): index is RoutePath;
32
49
  /**
33
50
  * 移除路径字符串中的指定后缀
34
51
  * @param path - 原始路径字符串