vitarx-router 1.0.2 → 2.0.0-beta.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.
- package/README.md +35 -1
- package/dist/scripts/type-make.d.ts +2 -2
- package/dist/scripts/type-make.js +2 -2
- package/dist/vitarx-router.d.ts +109 -53
- package/dist/vitarx-router.es.js +129 -51
- package/dist/vitarx-router.umd.js +1 -1
- package/package.json +20 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# VitarxRouter
|
|
2
2
|
|
|
3
|
-
Vitarx
|
|
3
|
+
`Vitarx` 前端框架的配套路由器[文档地址](https://vitarx.cn/router)
|
|
4
4
|
________________________________________________________________________
|
|
5
5
|
|
|
6
6
|
## 安装
|
|
@@ -9,3 +9,37 @@ ________________________________________________________________________
|
|
|
9
9
|
npm install vitarx-router
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
## 简单示例
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { defineRoutes, useRouter } from 'vitarx-router'
|
|
16
|
+
import AppHomePage from '@/pages/Home/index.js'
|
|
17
|
+
|
|
18
|
+
// 定义路由线路
|
|
19
|
+
const routes = defineRoutes(
|
|
20
|
+
{
|
|
21
|
+
path: '/',
|
|
22
|
+
name: 'home',
|
|
23
|
+
widget: AppHomePage
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: '/about',
|
|
27
|
+
name: 'about',
|
|
28
|
+
widget: () => import('@/pages/About/index.js') // 懒加载
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// 创建路由器
|
|
33
|
+
const router = createRouter({
|
|
34
|
+
routes: Routes,
|
|
35
|
+
mode: 'path', // 使用路径模式,除了此模式还支持 hash , memory 模式,path和hash模式只能在浏览器端使用,依赖 window.history Api
|
|
36
|
+
/** 其他配置查看文档 */
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// 导航
|
|
40
|
+
useRouter().push('/about') // 导航到路径
|
|
41
|
+
useRouter().push({ name: 'about' }) // 导航到路由名称
|
|
42
|
+
useRouter().psuh('about') // 不以/开头的字符串,也会被认为是路由名称
|
|
43
|
+
useRouter().replace('/about') // 替换路由,参数规则同push一致
|
|
44
|
+
```
|
|
45
|
+
|
|
@@ -12,8 +12,8 @@ declare interface Route {
|
|
|
12
12
|
*
|
|
13
13
|
* @param {Route[]} routes - 路由表
|
|
14
14
|
* @param {string[]} custom - 自定义的名称或路径,例如['/home', 'home']
|
|
15
|
-
* @param {string} [writePath] -
|
|
15
|
+
* @param {string} [writePath] - 要写入的路径,绝对路径!例如'/Users/Vitarx/vitarx/route.type.d.ts',默认为当前工作目录下的route.type.d.ts
|
|
16
16
|
* @return {void}
|
|
17
17
|
*/
|
|
18
|
-
export default function
|
|
18
|
+
export default function makeTypeRoute(routes: Route[], custom?: string[], writePath?: string): void;
|
|
19
19
|
export {};
|
|
@@ -52,10 +52,10 @@ function buildRouteIndex(routes) {
|
|
|
52
52
|
*
|
|
53
53
|
* @param {Route[]} routes - 路由表
|
|
54
54
|
* @param {string[]} custom - 自定义的名称或路径,例如['/home', 'home']
|
|
55
|
-
* @param {string} [writePath] -
|
|
55
|
+
* @param {string} [writePath] - 要写入的路径,绝对路径!例如'/Users/Vitarx/vitarx/route.type.d.ts',默认为当前工作目录下的route.type.d.ts
|
|
56
56
|
* @return {void}
|
|
57
57
|
*/
|
|
58
|
-
export default function
|
|
58
|
+
export default function makeTypeRoute(routes, custom = [], writePath = '') {
|
|
59
59
|
const { paths, names } = buildRouteIndex(routes);
|
|
60
60
|
const all = [...paths, ...names, ...custom];
|
|
61
61
|
// 构造类型声明字符串
|
package/dist/vitarx-router.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { App } from 'vitarx';
|
|
2
|
+
import { AppObjectPlugin } from 'vitarx';
|
|
1
3
|
import { Computed } from 'vitarx';
|
|
2
4
|
import { Element as Element_2 } from 'vitarx';
|
|
3
5
|
import { HTMLClassProperties } from 'vitarx';
|
|
4
6
|
import { HTMLProperties } from 'vitarx';
|
|
5
7
|
import { HTMLStyleProperties } from 'vitarx';
|
|
6
|
-
import { LazyLoader } from 'vitarx';
|
|
7
8
|
import { RouteLocation as RouteLocation_2 } from '../index.js';
|
|
8
9
|
import { VNode } from 'vitarx';
|
|
9
10
|
import { Widget } from 'vitarx';
|
|
@@ -17,6 +18,7 @@ import { WidgetType } from 'vitarx';
|
|
|
17
18
|
* @param this - 路由器实例
|
|
18
19
|
* @param to - 即将要进入的目标路由对象
|
|
19
20
|
* @param from - 当前导航正要离开的路由对象
|
|
21
|
+
* @returns {void}
|
|
20
22
|
*/
|
|
21
23
|
export declare type AfterEnterCallback = (this: Router, to: ReadonlyRouteLocation, from: ReadonlyRouteLocation) => void;
|
|
22
24
|
|
|
@@ -32,10 +34,11 @@ export declare type AllowedRouteWidget = RouteWidget | NamedRouteWidget;
|
|
|
32
34
|
*
|
|
33
35
|
* - boolean: true继续导航,false阻止导航
|
|
34
36
|
* - RouteTarget: 重定向到新的目标
|
|
37
|
+
* - string: 重定向到新的path/name
|
|
35
38
|
* - void: 继续导航
|
|
36
39
|
* - Promise: 异步处理,resolve的值同上
|
|
37
40
|
*/
|
|
38
|
-
export declare type BeforeEachCallbackResult = boolean | RouteTarget | void | Promise<boolean | RouteTarget | void>;
|
|
41
|
+
export declare type BeforeEachCallbackResult = boolean | RouteTarget | RouteIndex | void | Promise<boolean | RouteTarget | RouteIndex | void>;
|
|
39
42
|
|
|
40
43
|
/**
|
|
41
44
|
* 路由前置钩子
|
|
@@ -45,6 +48,7 @@ export declare type BeforeEachCallbackResult = boolean | RouteTarget | void | Pr
|
|
|
45
48
|
* @param this - 路由器实例
|
|
46
49
|
* @param to - 即将要进入的目标路由对象
|
|
47
50
|
* @param from - 当前导航正要离开的路由对象
|
|
51
|
+
* @returns {BeforeEachCallbackResult} 返回false可以取消导航
|
|
48
52
|
*/
|
|
49
53
|
export declare type BeforeEnterCallback = (this: Router, to: ReadonlyRouteLocation, from: ReadonlyRouteLocation) => BeforeEachCallbackResult;
|
|
50
54
|
|
|
@@ -56,26 +60,54 @@ export declare type BeforeEnterCallback = (this: Router, to: ReadonlyRouteLocati
|
|
|
56
60
|
* 你必须调用一次`router.replace(target)`方法来跳转到目标路由。
|
|
57
61
|
*
|
|
58
62
|
* @param {RouterOptions} options - 配置
|
|
59
|
-
* @return {RouterMemory} -
|
|
63
|
+
* @return {RouterMemory} - `RouterMemory`内存路由器实例
|
|
60
64
|
*/
|
|
61
|
-
export declare function createRouter(options: RouterOptions
|
|
62
|
-
mode: 'memory';
|
|
63
|
-
}): MemoryRouter;
|
|
65
|
+
export declare function createRouter(options: MakeRequired<RouterOptions<'memory'>, 'mode'>): MemoryRouter;
|
|
64
66
|
|
|
65
67
|
/**
|
|
66
|
-
* 创建
|
|
68
|
+
* 创建WebHistory路由器,path模式
|
|
67
69
|
*
|
|
68
70
|
* 基于History API实现路由功能,支持浏览器前进后退、刷新等操作。
|
|
69
71
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* 部署到服务器上时需要配置重定向,否则会出现404错误。
|
|
74
|
+
*
|
|
75
|
+
* nginx
|
|
76
|
+
* ```nginx
|
|
77
|
+
* location / {
|
|
78
|
+
* try_files $uri $uri/ /index.html;
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
* Apache
|
|
82
|
+
* ```Apache
|
|
83
|
+
* <IfModule mod_negotiation.c>
|
|
84
|
+
* Options -MultiViews
|
|
85
|
+
* </IfModule>
|
|
86
|
+
*
|
|
87
|
+
* <IfModule mod_rewrite.c>
|
|
88
|
+
* RewriteEngine On
|
|
89
|
+
* RewriteBase /
|
|
90
|
+
* RewriteRule ^index\.html$ - [L]
|
|
91
|
+
* RewriteCond %{REQUEST_FILENAME} !-f
|
|
92
|
+
* RewriteCond %{REQUEST_FILENAME} !-d
|
|
93
|
+
* RewriteRule . /index.html [L]
|
|
94
|
+
* </IfModule>
|
|
95
|
+
* ```
|
|
72
96
|
*
|
|
73
97
|
* @param {RouterOptions} options - 路由配置
|
|
74
|
-
* @return {RouterHistory} -
|
|
98
|
+
* @return {RouterHistory} - `RouterHistory`路由器实例
|
|
75
99
|
*/
|
|
76
|
-
export declare function createRouter(options: RouterOptions
|
|
77
|
-
|
|
78
|
-
|
|
100
|
+
export declare function createRouter(options: MakeRequired<RouterOptions<'path'>, 'mode'>): HistoryRouter;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 创建history路由器,hash模式
|
|
104
|
+
*
|
|
105
|
+
* 基于History API实现路由功能,支持浏览器前进后退、刷新等操作。
|
|
106
|
+
*
|
|
107
|
+
* @param {RouterOptions} options - 路由配置
|
|
108
|
+
* @return {RouterHistory} - `RouterHistory`路由器实例
|
|
109
|
+
*/
|
|
110
|
+
export declare function createRouter(options: RouterOptions<'hash'>): HistoryRouter;
|
|
79
111
|
|
|
80
112
|
/**
|
|
81
113
|
* 定义路由
|
|
@@ -103,6 +135,8 @@ export declare interface DynamicRouteRecord {
|
|
|
103
135
|
route: RouteNormalized;
|
|
104
136
|
}
|
|
105
137
|
|
|
138
|
+
declare type Hash = `#${string}`;
|
|
139
|
+
|
|
106
140
|
/**
|
|
107
141
|
* hash字符串类型
|
|
108
142
|
*
|
|
@@ -126,6 +160,14 @@ export declare type HistoryMode = 'hash' | 'path' | 'memory';
|
|
|
126
160
|
*/
|
|
127
161
|
export declare class HistoryRouter extends Router {
|
|
128
162
|
constructor(options: RouterOptions<'path' | 'hash'>);
|
|
163
|
+
/**
|
|
164
|
+
* @inheritDoc
|
|
165
|
+
*/
|
|
166
|
+
updateHash(hash: `#${string}` | ''): void;
|
|
167
|
+
/**
|
|
168
|
+
* @inheritDoc
|
|
169
|
+
*/
|
|
170
|
+
updateQuery(query: Record<string, string>): void;
|
|
129
171
|
/**
|
|
130
172
|
* 当前路由目标
|
|
131
173
|
*
|
|
@@ -204,40 +246,10 @@ export declare type InjectProps = boolean | Record<string, any> | InjectPropsHan
|
|
|
204
246
|
* 注入参数处理函数
|
|
205
247
|
*
|
|
206
248
|
* @param {ReadonlyRouteLocation} location 路由匹配的位置信息
|
|
207
|
-
* @return {Record<string, any>}
|
|
249
|
+
* @return {Record<string, any>} 注入的参数,必须是可JSON序列化的对象
|
|
208
250
|
*/
|
|
209
251
|
export declare type InjectPropsHandler = (location: ReadonlyRouteLocation) => Record<string, any>;
|
|
210
252
|
|
|
211
|
-
/**
|
|
212
|
-
* ## 标记延迟加载
|
|
213
|
-
*
|
|
214
|
-
* 由于直接定义`() => import('./xxx.js')`会导致类型与函数式组件冲突,
|
|
215
|
-
* 在未执行函数之前难以有效判断其类型,所以这里使用Symbol标记懒加载器。
|
|
216
|
-
*
|
|
217
|
-
* @example
|
|
218
|
-
* lazy(() => import('./xxx.js'))
|
|
219
|
-
*
|
|
220
|
-
* @template T - WidgetType
|
|
221
|
-
* @param {LazyLoader<T>} lazyLoader - 函数返回的import即是惰性加载器
|
|
222
|
-
*/
|
|
223
|
-
export declare function lazy<T extends WidgetType>(lazyLoader: LazyLoader<T>): LazyLoad<T>;
|
|
224
|
-
|
|
225
|
-
declare const LAZY_LOADER_SYMBOL: unique symbol;
|
|
226
|
-
|
|
227
|
-
declare type LAZY_LOADER_SYMBOL = typeof LAZY_LOADER_SYMBOL;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* 延迟加载/惰性加载
|
|
231
|
-
*
|
|
232
|
-
* 用于实现代码分块和懒加载功能
|
|
233
|
-
*/
|
|
234
|
-
export declare interface LazyLoad<T> {
|
|
235
|
-
[LAZY_LOADER_SYMBOL]: boolean;
|
|
236
|
-
(): Promise<{
|
|
237
|
-
default: T;
|
|
238
|
-
}>;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
253
|
/**
|
|
242
254
|
* 路由匹配结果
|
|
243
255
|
*/
|
|
@@ -413,7 +425,6 @@ export declare interface Route<T extends AllowedRouteWidget = AllowedRouteWidget
|
|
|
413
425
|
* 路由对应的视图组件
|
|
414
426
|
*
|
|
415
427
|
* 支持命名视图
|
|
416
|
-
*
|
|
417
428
|
* @example
|
|
418
429
|
* ```ts
|
|
419
430
|
* {
|
|
@@ -425,6 +436,15 @@ export declare interface Route<T extends AllowedRouteWidget = AllowedRouteWidget
|
|
|
425
436
|
* }
|
|
426
437
|
* }
|
|
427
438
|
* ```
|
|
439
|
+
*
|
|
440
|
+
* 支持懒加载
|
|
441
|
+
* @example
|
|
442
|
+
* ```ts
|
|
443
|
+
* {
|
|
444
|
+
* path: '/user/{id}',
|
|
445
|
+
* widget: () => import('./User.js')
|
|
446
|
+
* }
|
|
447
|
+
* ```
|
|
428
448
|
*/
|
|
429
449
|
widget?: T;
|
|
430
450
|
/**
|
|
@@ -568,7 +588,7 @@ export declare type RoutePath = `/${string}`;
|
|
|
568
588
|
* 继承自 RouterRegistry,负责处理路由导航、历史记录管理、生命周期等核心功能。
|
|
569
589
|
* 采用单例模式,确保全局只有一个路由器实例。
|
|
570
590
|
*/
|
|
571
|
-
export declare abstract class Router extends RouterRegistry {
|
|
591
|
+
export declare abstract class Router extends RouterRegistry implements AppObjectPlugin<{}> {
|
|
572
592
|
private _currentTaskId;
|
|
573
593
|
private _taskCounter;
|
|
574
594
|
/**
|
|
@@ -744,7 +764,7 @@ export declare abstract class Router extends RouterRegistry {
|
|
|
744
764
|
* @param {RouteTarget} target - 导航目标
|
|
745
765
|
* @return {Promise<NavigateResult>} 导航结果
|
|
746
766
|
*/
|
|
747
|
-
navigate(target: RouteTarget): Promise<NavigateResult>;
|
|
767
|
+
navigate(target: RouteTarget | RouteLocation): Promise<NavigateResult>;
|
|
748
768
|
/**
|
|
749
769
|
* 更新当前导航数据中的query参数
|
|
750
770
|
*
|
|
@@ -763,6 +783,14 @@ export declare abstract class Router extends RouterRegistry {
|
|
|
763
783
|
* @protected
|
|
764
784
|
*/
|
|
765
785
|
updateHash(hash: `#${string}` | ''): void;
|
|
786
|
+
/**
|
|
787
|
+
* 创建路由视图的props
|
|
788
|
+
*
|
|
789
|
+
* @param {ReadonlyRouteNormalized} route - 路由记录
|
|
790
|
+
* @param {string} name - 视图名称
|
|
791
|
+
* @returns {Record<string, any>} - 需要传递给路由视图的props
|
|
792
|
+
*/
|
|
793
|
+
createViewProps(route: ReadonlyRouteNormalized, name: string): Record<string, any>;
|
|
766
794
|
/**
|
|
767
795
|
* 创建路由元素
|
|
768
796
|
*
|
|
@@ -854,6 +882,26 @@ export declare abstract class Router extends RouterRegistry {
|
|
|
854
882
|
* @inheritDoc
|
|
855
883
|
*/
|
|
856
884
|
removeRoute(index: RouteIndex): RouteNormalized | undefined;
|
|
885
|
+
/**
|
|
886
|
+
* 安装路由器
|
|
887
|
+
*
|
|
888
|
+
* 此方法用于安装路由器,将路由器实例添加到应用程序中。
|
|
889
|
+
*
|
|
890
|
+
* 安装路由器后,应用程序就可以使用`app.get('router')`访问路由器实例。
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* import { createApp } from 'vitarx'
|
|
894
|
+
* import { createRouter } from 'vitarx'
|
|
895
|
+
* import AppHome from './App.js'
|
|
896
|
+
*
|
|
897
|
+
* const router = createRouter({// 相关配置})
|
|
898
|
+
* const app = createApp('#root').use(router)
|
|
899
|
+
* app.render(AppHome)
|
|
900
|
+
*
|
|
901
|
+
* @param {App} app - 应用程序实例
|
|
902
|
+
* @returns {void}
|
|
903
|
+
*/
|
|
904
|
+
install(app: App): void;
|
|
857
905
|
}
|
|
858
906
|
|
|
859
907
|
/**
|
|
@@ -870,7 +918,7 @@ export declare class RouterLink extends Widget<RouterLinkProps> {
|
|
|
870
918
|
*
|
|
871
919
|
* @protected
|
|
872
920
|
*/
|
|
873
|
-
protected target: Computed<RouteTarget | HttpUrl | undefined>;
|
|
921
|
+
protected target: Computed<RouteTarget | HttpUrl | Hash | undefined>;
|
|
874
922
|
/**
|
|
875
923
|
* 路由目标对应的`RouteLocation`对象
|
|
876
924
|
*
|
|
@@ -916,7 +964,7 @@ export declare interface RouterLinkProps {
|
|
|
916
964
|
*
|
|
917
965
|
* 可以是路由目标对象,也可以是路由索引
|
|
918
966
|
*/
|
|
919
|
-
to: RouteTarget | RouteIndex
|
|
967
|
+
to: RouteTarget | RouteIndex | HttpUrl | Hash | `${RouteIndex}${HashStr}`;
|
|
920
968
|
/**
|
|
921
969
|
* 子节点插槽
|
|
922
970
|
*/
|
|
@@ -1044,13 +1092,21 @@ export declare interface RouterOptions<T extends HistoryMode = HistoryMode> {
|
|
|
1044
1092
|
*/
|
|
1045
1093
|
pattern?: RegExp;
|
|
1046
1094
|
/**
|
|
1047
|
-
*
|
|
1095
|
+
* 切换页面时的滚动行为
|
|
1048
1096
|
*
|
|
1049
1097
|
* 可以是一个函数或行为标识符,决定了当路由变化时如何滚动页面
|
|
1050
1098
|
*
|
|
1051
|
-
* @default '
|
|
1099
|
+
* @default 'auto'
|
|
1052
1100
|
*/
|
|
1053
1101
|
scrollBehavior?: _ScrollBehavior | ScrollBehaviorHandler;
|
|
1102
|
+
/**
|
|
1103
|
+
* 锚点跳转时的滚动行为
|
|
1104
|
+
*
|
|
1105
|
+
* 仅支持 'auto' | 'smooth' | 'instant'
|
|
1106
|
+
*
|
|
1107
|
+
* @default 'auto'
|
|
1108
|
+
*/
|
|
1109
|
+
anchorsScrollBehavior?: _ScrollBehavior;
|
|
1054
1110
|
/**
|
|
1055
1111
|
* 在每个路由进入之前调用的钩子函数
|
|
1056
1112
|
* 允许用户在路由激活之前执行逻辑检查或重定向
|
|
@@ -1275,7 +1331,7 @@ export declare class RouterView extends Widget<RouteOptions> {
|
|
|
1275
1331
|
* ```tsx
|
|
1276
1332
|
* protected build() {
|
|
1277
1333
|
* // 使用空片段节点占位
|
|
1278
|
-
* if (!this.currentElement) return
|
|
1334
|
+
* if (!this.currentElement) return null // 不可省略,因为`KeepAlive`不能渲染非组件节点。
|
|
1279
1335
|
*
|
|
1280
1336
|
* // 将当前要进行展示的小部件构造函数传递给`KeepAlive`插槽,会在切换页面时缓存当前页面状态。
|
|
1281
1337
|
* return <KeepAlive>{this.currentElement}</KeepAlive>
|
|
@@ -1325,7 +1381,7 @@ export declare interface RouteTarget {
|
|
|
1325
1381
|
*
|
|
1326
1382
|
* 可以是普通组件或懒加载组件
|
|
1327
1383
|
*/
|
|
1328
|
-
export declare type RouteWidget = WidgetType
|
|
1384
|
+
export declare type RouteWidget = WidgetType;
|
|
1329
1385
|
|
|
1330
1386
|
/**
|
|
1331
1387
|
* 滚动行为
|
package/dist/vitarx-router.es.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
4
|
+
import { deepClone, reactive, markRaw, shallowReactive, readonly, createElement, isDeepEqual, isRecordObject, isObject, Widget, shallowRef, inject, provide, watch, toRaw, Observers, Fragment, Computed, isString } from "vitarx";
|
|
5
5
|
var NavigateStatus = /* @__PURE__ */ ((NavigateStatus2) => (NavigateStatus2[NavigateStatus2.success = 0] = "success", NavigateStatus2[NavigateStatus2.aborted = 1] = "aborted", NavigateStatus2[NavigateStatus2.cancelled = 2] = "cancelled", NavigateStatus2[NavigateStatus2.duplicated = 3] = "duplicated", NavigateStatus2[NavigateStatus2.not_matched = 4] = "not_matched", NavigateStatus2[NavigateStatus2.exception = 5] = "exception", NavigateStatus2))(NavigateStatus || {});
|
|
6
|
-
const LAZY_LOADER_SYMBOL = Symbol("LazyLoader");
|
|
7
6
|
function isVariablePath(path) {
|
|
8
7
|
return /\{[^}]+}/.test(path);
|
|
9
8
|
}
|
|
@@ -51,9 +50,6 @@ function mergePathParams(path, params) {
|
|
|
51
50
|
return String(params[paramName]).replace(/\s+/g, "_");
|
|
52
51
|
}), formatPath(path);
|
|
53
52
|
}
|
|
54
|
-
function isLazyLoad(lazyLoader) {
|
|
55
|
-
return typeof lazyLoader == "function" && lazyLoader[LAZY_LOADER_SYMBOL];
|
|
56
|
-
}
|
|
57
53
|
function formatHash(hash, addHashPrefix) {
|
|
58
54
|
return typeof hash != "string" || !hash ? "" : (hash = hash.trim(), hash.startsWith("#") ? hash : `#${hash}`);
|
|
59
55
|
}
|
|
@@ -104,14 +100,11 @@ function isRouteLocationTypeObject(obj) {
|
|
|
104
100
|
return !0;
|
|
105
101
|
}
|
|
106
102
|
function validateSuffix(suffix, allowSuffix, inputPath, routePath) {
|
|
107
|
-
return allowSuffix === "*" ? !0 : allowSuffix === !1 ? inputPath === routePath : Array.isArray(allowSuffix) ? allowSuffix.includes(suffix) : suffix === allowSuffix;
|
|
103
|
+
return inputPath === "/" || allowSuffix === "*" ? !0 : allowSuffix === !1 ? inputPath === routePath : Array.isArray(allowSuffix) ? allowSuffix.includes(suffix) : suffix === allowSuffix;
|
|
108
104
|
}
|
|
109
105
|
function addPathSuffix(path, suffix) {
|
|
110
106
|
return suffix && !path.endsWith("/") && !path.includes(".") && (path += suffix.startsWith(".") ? suffix : `.${suffix}`), path;
|
|
111
107
|
}
|
|
112
|
-
function createViewElement(widget, props) {
|
|
113
|
-
return isLazyLoad(widget) ? createElement(LazyWidget, { children: widget, ...props }) : createElement(widget, props);
|
|
114
|
-
}
|
|
115
108
|
function cloneRouteLocation(route) {
|
|
116
109
|
const { matched, ...other } = route;
|
|
117
110
|
return Object.assign(deepClone(other), { matched: Array.from(matched) });
|
|
@@ -188,7 +181,8 @@ class RouterRegistry {
|
|
|
188
181
|
base: "/",
|
|
189
182
|
strict: !1,
|
|
190
183
|
mode: "path",
|
|
191
|
-
scrollBehavior: "
|
|
184
|
+
scrollBehavior: "auto",
|
|
185
|
+
anchorsScrollBehavior: "auto",
|
|
192
186
|
suffix: "*",
|
|
193
187
|
pattern: /[\w.]+/,
|
|
194
188
|
defaultSuffix: "",
|
|
@@ -492,6 +486,15 @@ function diffUpdateObjects(a, b) {
|
|
|
492
486
|
for (const key of aKeys)
|
|
493
487
|
delete a[key];
|
|
494
488
|
}
|
|
489
|
+
function diffUpdateProps(oldProps, newProps) {
|
|
490
|
+
const removeKeys = new Set(Object.keys(oldProps)), bKeys = Object.keys(newProps), changes = [];
|
|
491
|
+
for (const key of bKeys)
|
|
492
|
+
key !== "key" && (oldProps[key] !== newProps[key] && (oldProps[key] = newProps[key], changes.push(key)), removeKeys.delete(key));
|
|
493
|
+
changes.push(...removeKeys);
|
|
494
|
+
for (const key of removeKeys)
|
|
495
|
+
delete oldProps[key];
|
|
496
|
+
return changes;
|
|
497
|
+
}
|
|
495
498
|
const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
496
499
|
/**
|
|
497
500
|
* 路由器构造函数
|
|
@@ -642,16 +645,14 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
642
645
|
/**
|
|
643
646
|
* 路由视图
|
|
644
647
|
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
647
|
-
* @internal
|
|
648
|
+
* @internal 内部核心方法,用于获取路由线路对应的视图元素虚拟节点。
|
|
648
649
|
* @param {ReadonlyRouteNormalized} route - 路由对象
|
|
649
650
|
* @param {string} name - 视图名称
|
|
650
651
|
* @param {number} index - `RouterView`的索引
|
|
651
652
|
* @return {VNode<WidgetType> | undefined} - 视图元素虚拟节点
|
|
652
653
|
*/
|
|
653
654
|
static routeViewElement(route, name, index) {
|
|
654
|
-
return route ? this.instance.createRouteViewElement(route, name) : index === 0 && name === "default" && this.instance.missing && !this.instance.isPendingNavigation ?
|
|
655
|
+
return route ? this.instance.createRouteViewElement(route, name) : index === 0 && name === "default" && this.instance.missing && !this.instance.isPendingNavigation ? createElement(this.instance.missing) : void 0;
|
|
655
656
|
}
|
|
656
657
|
/**
|
|
657
658
|
* 后退到上一个历史记录
|
|
@@ -752,7 +753,25 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
752
753
|
const taskId = ++this._taskCounter;
|
|
753
754
|
this._currentTaskId = taskId;
|
|
754
755
|
const isCurrentTask = () => this._currentTaskId === taskId, from = cloneRouteLocation(this.currentRouteLocation), performNavigation = async (_target, isRedirect) => {
|
|
755
|
-
const
|
|
756
|
+
const createNavigateResult = (overrides = {}) => ({
|
|
757
|
+
from,
|
|
758
|
+
to,
|
|
759
|
+
status: NavigateStatus.success,
|
|
760
|
+
message: "导航成功",
|
|
761
|
+
redirectFrom: isRedirect ? target : void 0,
|
|
762
|
+
...overrides
|
|
763
|
+
}), to = this.createRouteLocation(_target);
|
|
764
|
+
if (to.fullPath === this.currentRouteLocation.fullPath && isDeepEqual(to.params, this.currentRouteLocation.params))
|
|
765
|
+
return createNavigateResult({
|
|
766
|
+
status: NavigateStatus.duplicated,
|
|
767
|
+
message: "导航到相同的路由,被系统阻止!"
|
|
768
|
+
});
|
|
769
|
+
if (to.matched.at(-1) === this._currentRouteLocation.matched.at(-1) && to.path === this._currentRouteLocation.path && isDeepEqual(to.query, this._currentRouteLocation.query) && to.hash !== this._currentRouteLocation.hash)
|
|
770
|
+
return this.updateHash(to.hash), createNavigateResult({
|
|
771
|
+
status: NavigateStatus.success,
|
|
772
|
+
message: "仅hash变化,导航成功!"
|
|
773
|
+
});
|
|
774
|
+
const matched = to.matched.at(-1);
|
|
756
775
|
if (matched != null && matched.redirect) {
|
|
757
776
|
let redirectTarget;
|
|
758
777
|
if (typeof matched.redirect == "object" && matched.redirect.index)
|
|
@@ -765,28 +784,15 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
765
784
|
}
|
|
766
785
|
if (redirectTarget != null && redirectTarget.index) return performNavigation(redirectTarget, !0);
|
|
767
786
|
}
|
|
768
|
-
const createNavigateResult = (overrides = {}) => ({
|
|
769
|
-
from,
|
|
770
|
-
to,
|
|
771
|
-
status: NavigateStatus.success,
|
|
772
|
-
message: "导航成功",
|
|
773
|
-
redirectFrom: isRedirect ? target : void 0,
|
|
774
|
-
...overrides
|
|
775
|
-
});
|
|
776
|
-
if (to.fullPath === this.currentRouteLocation.fullPath)
|
|
777
|
-
return createNavigateResult({
|
|
778
|
-
status: NavigateStatus.duplicated,
|
|
779
|
-
message: "导航到相同的路由,被系统阻止!"
|
|
780
|
-
});
|
|
781
787
|
try {
|
|
782
788
|
const result = await this.onBeforeEach(to, from);
|
|
783
789
|
return result === !1 ? createNavigateResult({
|
|
784
790
|
status: NavigateStatus.aborted,
|
|
785
791
|
message: `导航到${to.index}目标时被前置守卫钩子阻止!`
|
|
786
|
-
}) : isCurrentTask() ? typeof result == "object" && result.index !== _target.index ? (result.isReplace ?? (result.isReplace = !1), performNavigation(result, !0)) : !to.matched.length && !this.missing ? createNavigateResult({
|
|
792
|
+
}) : isCurrentTask() ? typeof result == "object" && result.index !== _target.index ? (result.isReplace ?? (result.isReplace = !1), performNavigation(result, !0)) : typeof result == "string" && result !== _target.index ? performNavigation({ index: result, isReplace: !1 }, !0) : !to.matched.length && !this.missing ? createNavigateResult({
|
|
787
793
|
status: NavigateStatus.not_matched,
|
|
788
794
|
message: `未匹配到任何路由规则,被系统阻止!请检测目标索引(${to.index})是否已注册路由。`
|
|
789
|
-
}) : (_target.isReplace ? (this._pendingReplace = to, this.replaceHistory(to)) : (this._pendingPush = to, this.pushHistory(to)), createNavigateResult(
|
|
795
|
+
}) : ("isReplace" in _target && _target.isReplace ? (this._pendingReplace = to, this.replaceHistory(to)) : (this._pendingPush = to, this.pushHistory(to)), createNavigateResult(
|
|
790
796
|
!to.matched.length && this.missing ? {
|
|
791
797
|
status: NavigateStatus.not_matched,
|
|
792
798
|
message: `${to.fullPath}未匹配到任何路由规则,被系统允许访问,因为系统定义了missing视图。`
|
|
@@ -830,7 +836,8 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
830
836
|
* @protected
|
|
831
837
|
*/
|
|
832
838
|
updateHash(hash) {
|
|
833
|
-
typeof hash != "string"
|
|
839
|
+
if (typeof hash != "string")
|
|
840
|
+
throw new TypeError(`[Vitarx.Router.updateHash][WARN]:hash值只能是字符串类型,给定${hash}`);
|
|
834
841
|
const newHash = formatHash(hash);
|
|
835
842
|
newHash !== this._currentRouteLocation.hash && (this._currentRouteLocation.hash = newHash, this._currentRouteLocation.fullPath = this.makeFullPath(
|
|
836
843
|
this._currentRouteLocation.path,
|
|
@@ -839,6 +846,19 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
839
846
|
this._currentRouteLocation.suffix
|
|
840
847
|
));
|
|
841
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* 创建路由视图的props
|
|
851
|
+
*
|
|
852
|
+
* @param {ReadonlyRouteNormalized} route - 路由记录
|
|
853
|
+
* @param {string} name - 视图名称
|
|
854
|
+
* @returns {Record<string, any>} - 需要传递给路由视图的props
|
|
855
|
+
*/
|
|
856
|
+
createViewProps(route, name) {
|
|
857
|
+
var _a;
|
|
858
|
+
const injectProps = (_a = route.injectProps) == null ? void 0 : _a[name];
|
|
859
|
+
let props;
|
|
860
|
+
return injectProps === !0 ? props = JSON.parse(JSON.stringify(this._currentRouteLocation.params)) : injectProps === !1 ? props = {} : typeof injectProps == "function" ? props = injectProps(this.currentRouteLocation) : isRecordObject(injectProps) ? props = injectProps : props = {}, props;
|
|
861
|
+
}
|
|
842
862
|
/**
|
|
843
863
|
* 创建路由元素
|
|
844
864
|
*
|
|
@@ -847,12 +867,11 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
847
867
|
* @protected
|
|
848
868
|
*/
|
|
849
869
|
createRouteViewElement(route, name) {
|
|
850
|
-
var _a
|
|
870
|
+
var _a;
|
|
851
871
|
const widget = (_a = route.widget) == null ? void 0 : _a[name];
|
|
852
872
|
if (!widget) return;
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
return injectPropsConfig === !0 ? props = this._currentRouteLocation.params : injectPropsConfig === !1 ? props = {} : typeof injectPropsConfig == "function" ? props = injectPropsConfig(this.currentRouteLocation) : isRecordObject(injectPropsConfig) ? props = injectPropsConfig : props = {}, props.key = route.path, createViewElement(widget, props);
|
|
873
|
+
const props = this.createViewProps(route, name);
|
|
874
|
+
return props.key = route.path, createElement(widget, props);
|
|
856
875
|
}
|
|
857
876
|
/**
|
|
858
877
|
* 该方法提供给`RouterView`完成渲染时调用
|
|
@@ -955,6 +974,28 @@ const _RouterCore = class _RouterCore extends RouterRegistry {
|
|
|
955
974
|
}
|
|
956
975
|
return removed;
|
|
957
976
|
}
|
|
977
|
+
/**
|
|
978
|
+
* 安装路由器
|
|
979
|
+
*
|
|
980
|
+
* 此方法用于安装路由器,将路由器实例添加到应用程序中。
|
|
981
|
+
*
|
|
982
|
+
* 安装路由器后,应用程序就可以使用`app.get('router')`访问路由器实例。
|
|
983
|
+
*
|
|
984
|
+
* @example
|
|
985
|
+
* import { createApp } from 'vitarx'
|
|
986
|
+
* import { createRouter } from 'vitarx'
|
|
987
|
+
* import AppHome from './App.js'
|
|
988
|
+
*
|
|
989
|
+
* const router = createRouter({// 相关配置})
|
|
990
|
+
* const app = createApp('#root').use(router)
|
|
991
|
+
* app.render(AppHome)
|
|
992
|
+
*
|
|
993
|
+
* @param {App} app - 应用程序实例
|
|
994
|
+
* @returns {void}
|
|
995
|
+
*/
|
|
996
|
+
install(app) {
|
|
997
|
+
app.register("router", this);
|
|
998
|
+
}
|
|
958
999
|
};
|
|
959
1000
|
/**
|
|
960
1001
|
* 路由器单例实例
|
|
@@ -966,6 +1007,36 @@ class RouterHistory extends RouterCore {
|
|
|
966
1007
|
constructor(options) {
|
|
967
1008
|
["path", "hash"].includes(options.mode) || (options.mode = "hash"), super(options), options.mode === "hash" && this.ensureHash();
|
|
968
1009
|
}
|
|
1010
|
+
/**
|
|
1011
|
+
* @inheritDoc
|
|
1012
|
+
*/
|
|
1013
|
+
updateHash(hash) {
|
|
1014
|
+
if (super.updateHash(hash), this.saveCurrentScrollPosition(), this.webHistory.pushState(
|
|
1015
|
+
this.createState(this.currentRouteLocation),
|
|
1016
|
+
"",
|
|
1017
|
+
this.currentRouteLocation.fullPath
|
|
1018
|
+
), !hash.trim())
|
|
1019
|
+
window.scrollTo(0, 0);
|
|
1020
|
+
else {
|
|
1021
|
+
const anchorId = hash.startsWith("#") ? hash.slice(1) : hash;
|
|
1022
|
+
try {
|
|
1023
|
+
const element = window.document.getElementById(anchorId);
|
|
1024
|
+
element && element.scrollIntoView({ behavior: this.options.anchorsScrollBehavior });
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
console.error(`滚动到目标锚点${hash}失败`, e);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* @inheritDoc
|
|
1032
|
+
*/
|
|
1033
|
+
updateQuery(query) {
|
|
1034
|
+
super.updateQuery(query), this.webHistory.pushState(
|
|
1035
|
+
this.createState(this.currentRouteLocation),
|
|
1036
|
+
"",
|
|
1037
|
+
this.currentRouteLocation.fullPath
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
969
1040
|
/**
|
|
970
1041
|
* 当前路由目标
|
|
971
1042
|
*
|
|
@@ -1059,11 +1130,7 @@ class RouterHistory extends RouterCore {
|
|
|
1059
1130
|
if (res.status === NavigateStatus.not_matched) {
|
|
1060
1131
|
if (res.to.index.startsWith("/")) {
|
|
1061
1132
|
const anchorId = res.to.index.slice(1), element = window.document.getElementById(anchorId);
|
|
1062
|
-
element && element.scrollIntoView({ behavior: this.scrollBehavior }), this.updateHash(`#${anchorId}`)
|
|
1063
|
-
this.createState(this.currentRouteLocation),
|
|
1064
|
-
"",
|
|
1065
|
-
this.currentRouteLocation.fullPath
|
|
1066
|
-
);
|
|
1133
|
+
element && element.scrollIntoView({ behavior: this.scrollBehavior }), this.updateHash(`#${anchorId}`);
|
|
1067
1134
|
}
|
|
1068
1135
|
} else res.status === NavigateStatus.duplicated && this.webHistory.replaceState(
|
|
1069
1136
|
this.createState(this.currentRouteLocation),
|
|
@@ -1162,7 +1229,7 @@ function defineRoute(route) {
|
|
|
1162
1229
|
}
|
|
1163
1230
|
function createRouter(options) {
|
|
1164
1231
|
let router;
|
|
1165
|
-
return
|
|
1232
|
+
return typeof window > "u" && options.mode !== "memory" ? (console.warn("当前环境非浏览器端,强制使用内存模式路由"), options.mode = "memory", new RouterMemory(options).initialize()) : (options.mode === "memory" ? router = new RouterMemory(options) : router = new RouterHistory(options), router.initialize());
|
|
1166
1233
|
}
|
|
1167
1234
|
function useRouter() {
|
|
1168
1235
|
return RouterCore.instance;
|
|
@@ -1170,11 +1237,6 @@ function useRouter() {
|
|
|
1170
1237
|
function useRoute() {
|
|
1171
1238
|
return RouterCore.instance.currentRouteLocation;
|
|
1172
1239
|
}
|
|
1173
|
-
function lazy(lazyLoader) {
|
|
1174
|
-
return Object.defineProperty(lazyLoader, LAZY_LOADER_SYMBOL, {
|
|
1175
|
-
value: !0
|
|
1176
|
-
}), lazyLoader;
|
|
1177
|
-
}
|
|
1178
1240
|
const INDEX_SYMBOL = Symbol("RouterViewCounter");
|
|
1179
1241
|
class RouterView extends Widget {
|
|
1180
1242
|
constructor(props) {
|
|
@@ -1190,9 +1252,20 @@ class RouterView extends Widget {
|
|
|
1190
1252
|
this._$currentRoute,
|
|
1191
1253
|
this.name,
|
|
1192
1254
|
this._$index
|
|
1193
|
-
)
|
|
1255
|
+
);
|
|
1256
|
+
const router = useRouter();
|
|
1257
|
+
let paramStr = JSON.stringify(this.location.params);
|
|
1258
|
+
watch(this.location.matched, (_c, o) => {
|
|
1194
1259
|
const newRoute = o[this.index];
|
|
1195
1260
|
newRoute !== this._$currentRoute && (this._$currentRoute = newRoute, this._$currentElement.value = RouterCore.routeViewElement(newRoute, this.name, this._$index));
|
|
1261
|
+
}), watch(this.location.params, () => {
|
|
1262
|
+
if (!this._$currentRoute) return;
|
|
1263
|
+
const newParams = JSON.stringify(this.location.params);
|
|
1264
|
+
if (newParams !== paramStr) {
|
|
1265
|
+
paramStr = newParams;
|
|
1266
|
+
const newProps = router.createViewProps(this._$currentRoute, this.name), oldProps = toRaw(this._$currentElement.value.props), changes = diffUpdateProps(oldProps, newProps);
|
|
1267
|
+
changes.length > 0 && Observers.trigger(this._$currentElement.value.props, changes);
|
|
1268
|
+
}
|
|
1196
1269
|
});
|
|
1197
1270
|
}
|
|
1198
1271
|
/**
|
|
@@ -1273,7 +1346,7 @@ class RouterView extends Widget {
|
|
|
1273
1346
|
* ```tsx
|
|
1274
1347
|
* protected build() {
|
|
1275
1348
|
* // 使用空片段节点占位
|
|
1276
|
-
* if (!this.currentElement) return
|
|
1349
|
+
* if (!this.currentElement) return null // 不可省略,因为`KeepAlive`不能渲染非组件节点。
|
|
1277
1350
|
*
|
|
1278
1351
|
* // 将当前要进行展示的小部件构造函数传递给`KeepAlive`插槽,会在切换页面时缓存当前页面状态。
|
|
1279
1352
|
* return <KeepAlive>{this.currentElement}</KeepAlive>
|
|
@@ -1323,8 +1396,14 @@ class RouterLink extends Widget {
|
|
|
1323
1396
|
__publicField(this, "htmlProps");
|
|
1324
1397
|
this.target = new Computed(() => {
|
|
1325
1398
|
if (!this.props.to) return;
|
|
1326
|
-
const to = isString(props.to) ? { index: props.to } : props.to;
|
|
1327
|
-
|
|
1399
|
+
const to = isString(props.to) ? { index: decodeURIComponent(props.to) } : props.to;
|
|
1400
|
+
if (RouterLink.isHttpOrHttpsUrl(to.index) || to.index.startsWith("#"))
|
|
1401
|
+
return to.index;
|
|
1402
|
+
if (to.index.includes("#")) {
|
|
1403
|
+
const [index, hash] = to.index.split("#", 2);
|
|
1404
|
+
to.index = index, to.hash = `#${hash}`;
|
|
1405
|
+
}
|
|
1406
|
+
return to;
|
|
1328
1407
|
}), this.location = new Computed(() => {
|
|
1329
1408
|
if (!this.target.value || typeof this.target.value == "string") return;
|
|
1330
1409
|
const location = RouterCore.instance.createRouteLocation(this.target.value);
|
|
@@ -1409,7 +1488,6 @@ export {
|
|
|
1409
1488
|
createRouter,
|
|
1410
1489
|
defineRoute,
|
|
1411
1490
|
defineRoutes,
|
|
1412
|
-
lazy,
|
|
1413
1491
|
useRoute,
|
|
1414
1492
|
useRouter
|
|
1415
1493
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(global,factory){typeof exports=="object"&&typeof module<"u"?factory(exports,require("vitarx")):typeof define=="function"&&define.amd?define(["exports","vitarx"],factory):(global=typeof globalThis<"u"?globalThis:global||self,factory(global.VitarxRouter={},global.Vitarx))})(this,function(exports2,vitarx){"use strict";var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value}):obj[key]=value;var __publicField=(obj,key,value)=>__defNormalProp(obj,typeof key!="symbol"?key+"":key,value);var NavigateStatus=(NavigateStatus2=>(NavigateStatus2[NavigateStatus2.success=0]="success",NavigateStatus2[NavigateStatus2.aborted=1]="aborted",NavigateStatus2[NavigateStatus2.cancelled=2]="cancelled",NavigateStatus2[NavigateStatus2.duplicated=3]="duplicated",NavigateStatus2[NavigateStatus2.not_matched=4]="not_matched",NavigateStatus2[NavigateStatus2.exception=5]="exception",NavigateStatus2))(NavigateStatus||{});const LAZY_LOADER_SYMBOL=Symbol("LazyLoader");function isVariablePath(path){return/\{[^}]+}/.test(path)}function optionalVariableCount(path){const pathWithoutSpaces=path.replace(/\s+/g,""),regex=/\{[\w-]+\?}/g,matches=pathWithoutSpaces.match(regex);return matches?matches.length:0}function isRouteGroup(route){return"children"in route&&route.children!==void 0&&route.children.length>0}function createDynamicPattern(path,pattern,strict,defaultPattern){let optional=0;const processVariable=(varName,isOptional)=>{const regex=pattern[varName];if(regex?regex instanceof RegExp||(console.warn(`[Vitarx.Router][WARN]:${path} 动态路径${varName}变量的自定义正则表达式必须是 RegExp 类型`),pattern[varName]=defaultPattern):pattern[varName]=defaultPattern,isOptional)return optional++,`(?:(${pattern[varName].source}))?`;if(optional)throw new Error(`[Vitarx.Router][ERROR]:动态路径 ${path} 中,可选变量 ${varName} 后不能存在任何必填变量`);return`(${pattern[varName].source})`},processedPath=path.replace(/{([^}?]+)\?}/g,(_,varName)=>processVariable(varName,!0)).replace(/{([^}]+)}/g,(_,varName)=>processVariable(varName,!1)).replace(/\//g,"\\/").replace(/\/?$/,"/?"),flags=strict?"":"i",segments=path.replace(/^\/|\/$/g,"").split("/").length;return{regex:new RegExp(`^${processedPath}$`,flags),length:segments,optional}}function formatPath(path){return path=`/${path}`.replace(/\s+/g,"").replace(/\/+/g,"/"),path.length?path==="/"||path==="/#/"?path:path.replace(/\/$/,""):"/"}function mergePathParams(path,params){if(!isVariablePath(path))return path;const oldPath=path;return path=path.replace(/{([^}]+)\?*}/g,(_match,paramName)=>{const isOptional=paramName.endsWith("?");if(isOptional&&(paramName=paramName.slice(0,-1)),params[paramName]===void 0){if(isOptional)return"";throw new TypeError(`[Vitarx.Router.mergePathParams] 访问路由${oldPath}时缺少参数:${paramName}`)}return String(params[paramName]).replace(/\s+/g,"_")}),formatPath(path)}function isLazyLoad(lazyLoader){return typeof lazyLoader=="function"&&lazyLoader[LAZY_LOADER_SYMBOL]}function formatHash(hash,addHashPrefix){return typeof hash!="string"||!hash?"":(hash=hash.trim(),hash.startsWith("#")?hash:`#${hash}`)}function queryStringToObject(queryString){queryString=decodeURIComponent(queryString);const params=new URLSearchParams(queryString.startsWith("?")?queryString.substring(1):queryString),obj={};return params.forEach((value,key)=>obj[key]=value),obj}function objectToQueryString(obj){const queryString=new URLSearchParams(obj).toString();return queryString?`?${queryString}`:""}function urlToRouteTarget(url,mode,base){let path=decodeURIComponent(url.pathname),hash=decodeURIComponent(url.hash),query=queryStringToObject(url.search);if(path=formatPath(path.startsWith(base)?path.slice(base.length):path),mode==="hash"&&hash.includes("#/")){const hashPart=hash.slice(1),[fullPath,anchor]=hashPart.split("#");path=formatPath(fullPath||"/"),hash=anchor?`#${anchor}`:""}return{index:path,hash,query}}function splitPathAndSuffix(path){const suffix=getPathSuffix(path);return suffix&&(path=path.slice(0,-suffix.length)),{path,suffix:suffix.substring(1)}}function getPathSuffix(path){const lastDotIndex=path.lastIndexOf(".");return lastDotIndex!==-1&&lastDotIndex<path.length-1?`.${path.substring(lastDotIndex+1)}`:""}function isRouteLocationTypeObject(obj){if(typeof obj!="object"||obj===null)return!1;const keys=["index","fullPath","path","hash","params","query","matched","meta"];for(const key of keys)if(!Object.prototype.hasOwnProperty.call(obj,key))return!1;return!0}function validateSuffix(suffix,allowSuffix,inputPath,routePath){return allowSuffix==="*"?!0:allowSuffix===!1?inputPath===routePath:Array.isArray(allowSuffix)?allowSuffix.includes(suffix):suffix===allowSuffix}function addPathSuffix(path,suffix){return suffix&&!path.endsWith("/")&&!path.includes(".")&&(path+=suffix.startsWith(".")?suffix:`.${suffix}`),path}function createViewElement(widget,props){return isLazyLoad(widget)?vitarx.createElement(vitarx.LazyWidget,{children:widget,...props}):vitarx.createElement(widget,props)}function cloneRouteLocation(route){const{matched,...other}=route;return Object.assign(vitarx.deepClone(other),{matched:Array.from(matched)})}const validInjectProps=props=>["boolean","function"].includes(typeof props);function normalizeInjectProps(route){if(!route.widget)return;const injectProps={},inputValue=route.injectProps??!0,type=typeof inputValue;if(type==="object")for(const name in route.widget){const value=inputValue[name];if(value&&!validInjectProps(value))throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由线路配置,injectProps.${name}值错误,仅支持boolean、function类型`);injectProps[name]=value??!0}else if(type==="function"||type==="boolean")for(const name in route.widget)injectProps[name]=inputValue;else throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由线路配置,injectProps属性配置错误,仅支持boolean、{key:boolean|function}、function类型`);route.injectProps=injectProps}const validWidgetType=(widget,path)=>{const type=typeof widget;if(type!=="function"){if(type==="object"){if(!widget.default)throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${path} 路由配置,widget传入对象时则认为是命名视图,命名视图必须具有default视图`);for(const k in widget)validWidgetType(widget[k],path);return}throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${path} 路由配置,widget配置无效`)}};function normalizeRouteWidget(route){const isGroup=route.children.length;if(route.widget)validWidgetType(route.widget,route.path);else if(!isGroup)throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由配置,widget和children不能同时为空。`);typeof route.widget=="function"&&(route.widget={default:route.widget})}function normalizeRoute(route,group,suffix){if(route.meta=route.meta||{},route.pattern=route.pattern||{},route.children=route.children||[],!Array.isArray(route.children))throw new TypeError(`[Vitarx.Router][TYPE_ERROR]:${route.path} 路由线路配置 children 类型错误,它必须是数组类型。`);if(!route.path.trim())throw new TypeError("[Vitarx.Router][TYPE_ERROR]:路由线路配置 path 不能为空");return route.path=formatPath(group?`${group.path}/${route.path}`:route.path),normalizeRouteWidget(route),normalizeInjectProps(route),route.suffix??(route.suffix=(group==null?void 0:group.suffix)??suffix),route.afterEnter??(route.afterEnter=group==null?void 0:group.afterEnter),route.beforeEnter??(route.beforeEnter=group==null?void 0:group.beforeEnter),route}class RouterRegistry{constructor(options){__publicField(this,"_options");__publicField(this,"_namedRoutes",new Map);__publicField(this,"_dynamicRoutes",new Map);__publicField(this,"_pathRoutes",new Map);__publicField(this,"_parentRoute",new WeakMap);const config={base:"/",strict:!1,mode:"path",scrollBehavior:"smooth",suffix:"*",pattern:/[\w.]+/,defaultSuffix:"",...options};if(config.base=`/${config.base.replace(/^\/+|\/+$/g,"")}`,typeof config.suffix=="string"?config.suffix=config.suffix.replace(/\./g,""):Array.isArray(config.suffix)&&(config.suffix=config.suffix.map(item=>item.replace(/\./g,""))),config.defaultSuffix&&(config.defaultSuffix=config.defaultSuffix.replace(/\./g,"")),config.missing&&typeof config.missing!="function")throw new TypeError("[VitarxRouter][ERROR]:missing配置无效");this._options=config}get options(){return this._options}get mode(){return this._options.mode}get missing(){return this._options.missing}get pathRoutes(){return this._pathRoutes}get namedRoutes(){return this._namedRoutes}get dynamicRoutes(){return this._dynamicRoutes}get routes(){return this._options.routes}get basePath(){return this._options.base}get suffix(){return this._options.suffix}removeRoute(index){const deleteRoute=this.findRoute(index);if(deleteRoute)return this._pathRoutes.delete(deleteRoute.path),deleteRoute.name&&this._namedRoutes.delete(deleteRoute.name),this.removeDynamicRoute(deleteRoute.path),this.removedFromRoutes(deleteRoute),deleteRoute}addRoute(route,parent){if(parent){const parentRoute=this.findRoute(parent);if(!parentRoute)throw new Error(`[Vitarx.Router.addRoute][ERROR]:父路由${parent}不存在`);parentRoute.children.includes(parentRoute)||parentRoute.children.push(this.registerRoute(route,parentRoute))}else this.registerRoute(route),this._options.routes.push(route)}findRoute(target){const isRouterTarget=typeof target=="object",index=isRouterTarget?target.index:target;if(typeof index!="string")throw new TypeError(`[Vitarx.Router.getRoute][ERROR]:路由索引${target}类型错误,必须给定字符串类型`);if(index.startsWith("/")){const matched=this.matchRoute(index);return matched?(matched.params&&isRouterTarget&&(target.params=Object.assign(target.params||{},matched.params)),matched.route):void 0}return this.findNamedRoute(index)}findPathRoute(path){return this._options.strict||(path=path.toLowerCase()),this._pathRoutes.get(path)}findNamedRoute(name){return this._namedRoutes.get(name)}findParentRoute(route){return this._parentRoute.get(route)}matchRoute(path){let formattedPath=formatPath(path);this._options.strict||(formattedPath=formattedPath.toLowerCase());try{const{path:shortPath,suffix}=splitPathAndSuffix(formattedPath),staticRoute=this._pathRoutes.get(shortPath);if(staticRoute&&validateSuffix(suffix,staticRoute.suffix,formattedPath,staticRoute.path))return{route:staticRoute,params:void 0};const segmentCount=shortPath.split("/").filter(Boolean).length,candidates=this._dynamicRoutes.get(segmentCount);if(candidates){const normalizedPath=`${shortPath}/`,regexCache=new Map;for(const{regex,route}of candidates){const match=(regexCache.get(regex.source)||regex).exec(normalizedPath);if(!match)continue;const params={},keys=Object.keys(route.pattern);for(let i=0;i<keys.length;i++)params[keys[i]]=match[i+1];if(validateSuffix(suffix,route.suffix,formattedPath,formattedPath))return{route,params}}}if(!suffix&&!shortPath.endsWith("/index")){const indexRoute=this._pathRoutes.get(`${shortPath==="/"?"":shortPath}/index`);if(indexRoute&&validateSuffix(suffix,indexRoute.suffix,formattedPath,indexRoute.path))return{route:indexRoute,params:void 0}}else if(shortPath==="/index"){const indexRoute=this._pathRoutes.get("/");if(indexRoute&&validateSuffix(suffix,indexRoute.suffix,formattedPath,indexRoute.path))return{route:indexRoute,params:void 0}}}catch(error){console.error("Error in matchRoute:",error);return}}setupRoutes(routes){for(const route of routes)this.registerRoute(route)}removedFromRoutes(route){const parent=this.findParentRoute(route);if(parent!=null&&parent.children){const index2=parent.children.indexOf(route);index2!==-1&&parent.children.splice(index2,1)}const index=this._options.routes.indexOf(route);index!==-1&&this._options.routes.splice(index,1)}removeDynamicRoute(path){if(!isVariablePath(path))return;const length=path.split("/").filter(Boolean).length,removeRouteFromRecords=key=>{const records=this._dynamicRoutes.get(key);if(records){for(let i=0;i<records.length;i++)if(records[i].route.path===path){records.splice(i,1);break}}};removeRouteFromRecords(length);const count=optionalVariableCount(path);if(count>0)for(let i=1;i<=count;i++)removeRouteFromRecords(length-i)}registerRoute(route,group){const normalizedRoute=normalizeRoute(route,group,this.suffix);if(group&&this._parentRoute.set(normalizedRoute,group),isRouteGroup(normalizedRoute)){this.recordRoute(normalizedRoute);for(const child of normalizedRoute.children)this.registerRoute(child,normalizedRoute);normalizedRoute.redirect||(normalizedRoute.redirect=function(to){var _a;let first=normalizedRoute.children[0];for(;first;){if(first.redirect)return typeof first.redirect=="function"?first.redirect.call(this,to):first.redirect;if(first.widget)return{index:first.path};first=(_a=first.children)==null?void 0:_a[0]}console.error(`[Vitarx.Router][ERROR]:${normalizedRoute.path} 分组路由在没有配置重定向的情况下,它的第一个子路由必须具有widget或redirect,否则无法匹配视图`,normalizedRoute)})}else this.recordRoute(normalizedRoute);return normalizedRoute}strictPath(path){return this._options.strict?path:path.toLowerCase()}recordRoute(route){if(route.name){if(route.name.startsWith("/")&&(route.name=route.name.replace(/^\//,""),console.warn(`[Vitarx.Router][WARN]:命名路由(name)不要以/开头: ${route.name},因为内部需要使用/区分path、name`)),this._namedRoutes.has(route.name))throw new Error(`[Vitarx.Router][ERROR]:检测到重复的路由名称(name): ${route.name}`);this._namedRoutes.set(route.name,route)}const path=this.strictPath(route.path);if(this._pathRoutes.has(path))throw new Error(`[Vitarx.Router][ERROR]:检测到重复的路由路径(path): ${route.path}`);this._pathRoutes.set(path,route),isVariablePath(route.path)&&this.recordDynamicRoute(route)}recordDynamicRoute(route){const{regex,length,optional}=createDynamicPattern(route.path,route.pattern,this.options.strict,this.options.pattern),addToLengthMap=len=>{this._dynamicRoutes.has(len)||this._dynamicRoutes.set(len,[]),this._dynamicRoutes.get(len).push({regex,route})};if(addToLengthMap(length),optional>0)for(let i=1;i<=optional;i++)addToLengthMap(length-i)}}const __stringValueKeys=["path","hash","index","fullPath"];function patchUpdate(location,newLocation){diffUpdateArrays(location.matched,newLocation.matched),diffUpdateObjects(location.params,newLocation.params),diffUpdateObjects(location.query,newLocation.query),diffUpdateObjects(location.meta,newLocation.meta);for(const key of __stringValueKeys)location[key]!==newLocation[key]&&(location[key]=newLocation[key])}function diffUpdateArrays(a,b){for(let i=0;i<b.length;i++)a[i]!==b[i]&&(a[i]=b[i]);a.length>b.length&&(a.length=b.length)}function diffUpdateObjects(a,b){const aKeys=new Set(Object.keys(a)),bKeys=Object.keys(b);for(const key of bKeys)a[key]=b[key],aKeys.delete(key);for(const key of aKeys)delete a[key]}const _RouterCore=class _RouterCore extends RouterRegistry{constructor(options){if(_RouterCore._instance)throw new Error("[Vitarx.Router.constructor]:路由器实例已存在,不能创建多个实例");super(options);__publicField(this,"_currentTaskId",null);__publicField(this,"_taskCounter",0);__publicField(this,"_pendingReplace",null);__publicField(this,"_pendingPush",null);__publicField(this,"_scrollBehaviorHandler");__publicField(this,"_currentRouteLocation");__publicField(this,"_readonlyRouteLocation");__publicField(this,"_isBrowser",typeof window<"u"&&typeof window.document<"u");__publicField(this,"_scrollBehavior","auto");this._currentRouteLocation=vitarx.reactive({index:this._options.base,path:this._options.base,hash:"",fullPath:"",params:{},query:{},matched:vitarx.shallowReactive([]),meta:vitarx.markRaw({}),suffix:""}),this._readonlyRouteLocation=vitarx.readonly(this._currentRouteLocation)}static get instance(){if(!_RouterCore._instance)throw new Error("[Vitarx.Router.instance]:路由器实例未初始化");return _RouterCore._instance}get initialized(){return _RouterCore._instance!==void 0}get isBrowser(){return this._isBrowser}get scrollBehavior(){return this._scrollBehavior}get options(){return this._options}get mode(){return this._options.mode}get currentRouteLocation(){return this._readonlyRouteLocation}get isPendingNavigation(){return!!(this._pendingReplace||this._pendingPush)}get pendingReplaceData(){return this._pendingReplace}get pendingPushData(){return this._pendingPush}static routeViewElement(route,name,index){return route?this.instance.createRouteViewElement(route,name):index===0&&name==="default"&&this.instance.missing&&!this.instance.isPendingNavigation?createViewElement(this.instance.missing,{}):void 0}back(){return this.go(-1)}forward(){return this.go(1)}replace(target){return typeof target=="string"?this.navigate({index:target,isReplace:!0}):(target.isReplace=!0,this.navigate(target))}push(target){return typeof target=="string"?this.navigate({index:target,isReplace:!1}):(target.isReplace=!1,this.navigate(target))}initialize(){return _RouterCore._instance?this:(this.setupRoutes(this._options.routes),typeof this.options.scrollBehavior=="function"?this._scrollBehaviorHandler=this.options.scrollBehavior:this._scrollBehavior=this.options.scrollBehavior,this.initializeRouter(),_RouterCore._instance=this,this)}scrollTo(scrollTarget){if(!(!this.isBrowser||!scrollTarget||typeof scrollTarget!="object"))try{if("el"in scrollTarget){const{el,...rest}=scrollTarget,element=typeof el=="string"?document.querySelector(el):el;element&&element instanceof Element&&(element.scrollIntoView?element.scrollIntoView({behavior:this.scrollBehavior,...rest}):window.scrollTo({behavior:this.scrollBehavior,top:element.getBoundingClientRect().top+window.scrollY,left:element.getBoundingClientRect().left+window.scrollX}));return}window.scrollTo({behavior:this.scrollBehavior,...scrollTarget})}catch(e){console.error("[Vitarx.Router.scrollTo][WARN]:滚动到指定位置时发生错误,请检查滚动目标参数是否正确",e)}}createRouteLocation(target){if(isRouteLocationTypeObject(target))return target;const route=this.findRoute(target),{index,query={},params={},hash=""}=target,path=route?mergePathParams(route.path,params):formatPath(index),matched=[];if(route){let parent=this.findParentRoute(route);for(;parent;)parent.widget&&matched.unshift(parent),parent=this.findParentRoute(parent);matched.push(route)}const meta=route!=null&&route.meta?vitarx.deepClone(route.meta):{},hashStr=formatHash(hash),suffix=getPathSuffix(index),fullPath=this.makeFullPath(path,query,hashStr,suffix);return{index,path,hash:hashStr,fullPath,params,query,matched,meta,suffix}}navigate(target){const taskId=++this._taskCounter;this._currentTaskId=taskId;const isCurrentTask=()=>this._currentTaskId===taskId,from=cloneRouteLocation(this.currentRouteLocation),performNavigation=async(_target,isRedirect)=>{const to=this.createRouteLocation(_target),matched=to.matched.at(-1);if(matched!=null&&matched.redirect){let redirectTarget;if(typeof matched.redirect=="object"&&matched.redirect.index)redirectTarget=matched.redirect;else if(typeof matched.redirect=="string")redirectTarget={index:matched.redirect};else if(typeof matched.redirect=="function"){const redirectHandleResult=matched.redirect.call(this,to);vitarx.isObject(redirectHandleResult)&&(redirectTarget=redirectHandleResult)}if(redirectTarget!=null&&redirectTarget.index)return performNavigation(redirectTarget,!0)}const createNavigateResult=(overrides={})=>({from,to,status:NavigateStatus.success,message:"导航成功",redirectFrom:isRedirect?target:void 0,...overrides});if(to.fullPath===this.currentRouteLocation.fullPath)return createNavigateResult({status:NavigateStatus.duplicated,message:"导航到相同的路由,被系统阻止!"});try{const result=await this.onBeforeEach(to,from);return result===!1?createNavigateResult({status:NavigateStatus.aborted,message:`导航到${to.index}目标时被前置守卫钩子阻止!`}):isCurrentTask()?typeof result=="object"&&result.index!==_target.index?(result.isReplace??(result.isReplace=!1),performNavigation(result,!0)):!to.matched.length&&!this.missing?createNavigateResult({status:NavigateStatus.not_matched,message:`未匹配到任何路由规则,被系统阻止!请检测目标索引(${to.index})是否已注册路由。`}):(_target.isReplace?(this._pendingReplace=to,this.replaceHistory(to)):(this._pendingPush=to,this.pushHistory(to)),createNavigateResult(!to.matched.length&&this.missing?{status:NavigateStatus.not_matched,message:`${to.fullPath}未匹配到任何路由规则,被系统允许访问,因为系统定义了missing视图。`}:void 0)):createNavigateResult({status:NavigateStatus.cancelled,message:"已被新的导航请求替代,取消此次导航!"})}catch(error){return console.error("[Vitarx.Router.navigate][ERROR]:导航时捕获到了异常",error),createNavigateResult({status:NavigateStatus.exception,message:"导航时捕获到了异常",error})}};return performNavigation(target,!1)}updateQuery(query){vitarx.isDeepEqual(this._currentRouteLocation.query,query)||(this._currentRouteLocation.query=query,this._currentRouteLocation.fullPath=this.makeFullPath(this._currentRouteLocation.path,query,this._currentRouteLocation.hash,this._currentRouteLocation.suffix))}updateHash(hash){typeof hash!="string"&&console.warn(`[Vitarx.Router.updateHash][WARN]:hash值只能是字符串类型,给定${hash}`);const newHash=formatHash(hash);newHash!==this._currentRouteLocation.hash&&(this._currentRouteLocation.hash=newHash,this._currentRouteLocation.fullPath=this.makeFullPath(this._currentRouteLocation.path,this._currentRouteLocation.query,newHash,this._currentRouteLocation.suffix))}createRouteViewElement(route,name){var _a,_b;const widget=(_a=route.widget)==null?void 0:_a[name];if(!widget)return;const injectPropsConfig=(_b=route.injectProps)==null?void 0:_b[name];let props;return injectPropsConfig===!0?props=this._currentRouteLocation.params:injectPropsConfig===!1?props={}:typeof injectPropsConfig=="function"?props=injectPropsConfig(this.currentRouteLocation):vitarx.isRecordObject(injectPropsConfig)?props=injectPropsConfig:props={},props.key=route.path,createViewElement(widget,props)}_completeViewRender(){}completeNavigation(savedPosition){const newData=this.pendingReplaceData||this.pendingPushData;if(!newData)throw new Error("[Vitarx.Router.completeNavigation][ERROR]:没有处于等待状态的导航请求。");const from=cloneRouteLocation(this.currentRouteLocation);this._completeViewRender=()=>{this.onScrollBehavior(this.currentRouteLocation,from,savedPosition).then(),this.onAfterEach(this.currentRouteLocation,from)},this.updateRouteLocation(newData),this._pendingReplace=null,this._pendingPush=null}makeFullPath(path,query,hash,suffix){return hash&&!hash.startsWith("#")&&(hash=`#${hash}`),typeof query=="object"&&(query=objectToQueryString(query)),path=addPathSuffix(path,suffix||this._options.defaultSuffix),this.mode==="hash"?formatPath(`${this.basePath}/${query}#${path}${hash}`):formatPath(`${this.basePath}${path}${query}${hash}`)}onBeforeEach(to,from){var _a;const matched=to.matched.at(-1);return matched&&"beforeEnter"in matched&&typeof matched.beforeEnter=="function"?matched.beforeEnter.call(this,to,from):(_a=this._options.beforeEach)==null?void 0:_a.call(this,to,from)}onAfterEach(to,from){var _a;const matched=to.matched.at(-1);if(matched)return"afterEnter"in matched&&typeof matched.afterEnter=="function"?matched.afterEnter.call(this,to,from):(_a=this._options.afterEach)==null?void 0:_a.call(this,to,from)}updateRouteLocation(newLocation){patchUpdate(this._currentRouteLocation,newLocation)}async onScrollBehavior(to,from,savedPosition){try{if(this._scrollBehaviorHandler){const scrollTarget=await this._scrollBehaviorHandler(to,from,savedPosition);scrollTarget&&this.scrollTo(scrollTarget)}else!savedPosition&&!to.hash?this.scrollTo({left:0,top:0}):this.scrollTo(savedPosition??{el:to.hash})}catch(e){console.error("[Vitarx.Router.onScrollBehavior]['ERROR']:处理滚动行为时捕获到了异常",e)}}removeRoute(index){const removed=super.removeRoute(index);if(removed){const index2=this._currentRouteLocation.matched.indexOf(removed);index2!==-1&&this._currentRouteLocation.matched.splice(index2,1)}return removed}};__publicField(_RouterCore,"_instance");let RouterCore=_RouterCore;class RouterHistory extends RouterCore{constructor(options){["path","hash"].includes(options.mode)||(options.mode="hash"),super(options),options.mode==="hash"&&this.ensureHash()}get currentRouteTarget(){return urlToRouteTarget(window.location,this.mode,this.basePath)}get webHistory(){return window.history}go(delta){this.webHistory.go(delta)}initializeRouter(){window.addEventListener("popstate",this.onPopState.bind(this)),this.replace(this.currentRouteTarget).then(res=>{res.status!==NavigateStatus.success&&console.warn(`[VitarxRouter.initializeRouter]:路由初始化匹配失败,${res.message}`)})}pushHistory(data){this.saveCurrentScrollPosition(),this.webHistory.pushState(this.createState(data),"",data.fullPath),this.completeNavigation()}replaceHistory(data){var _a;const scrollPosition=(_a=this.webHistory.state)==null?void 0:_a.scrollPosition;this.webHistory.replaceState(this.createState(data),"",data.fullPath),this.completeNavigation(scrollPosition)}saveCurrentScrollPosition(){const scrollPosition={left:window.scrollX,top:window.scrollY,behavior:this.scrollBehavior};this.webHistory.replaceState({...this.webHistory.state,scrollPosition},"",window.location.href)}createState(data,hash,query){const{matched,...state}=data;return typeof hash=="string"&&(state.hash=hash),typeof query=="object"&&(state.query=query),JSON.parse(JSON.stringify(state))}onPopState(event){var _a;let newTarget;(_a=event.state)!=null&&_a.index?newTarget={index:event.state.index,hash:event.state.hash,query:event.state.query}:newTarget=this.currentRouteTarget,this.replace(newTarget).then(res=>{if(!res.redirectFrom&&this.mode==="hash"&&res.status!==NavigateStatus.success)if(res.status===NavigateStatus.not_matched){if(res.to.index.startsWith("/")){const anchorId=res.to.index.slice(1),element=window.document.getElementById(anchorId);element&&element.scrollIntoView({behavior:this.scrollBehavior}),this.updateHash(`#${anchorId}`),this.webHistory.replaceState(this.createState(this.currentRouteLocation),"",this.currentRouteLocation.fullPath)}}else res.status===NavigateStatus.duplicated&&this.webHistory.replaceState(this.createState(this.currentRouteLocation),"",this.currentRouteLocation.fullPath)})}ensureHash(){const{pathname,search,hash}=window.location;if(!hash){const path=`${this.basePath}${search}#${pathname}`;window.location.replace(path)}}}class RouterMemory extends RouterCore{constructor(options){super(options);__publicField(this,"_history",[]);__publicField(this,"_pendingGo",null);__publicField(this,"_currentIndex",0);options.mode="memory"}get currentIndex(){return this._currentIndex}go(delta){if(!delta)return;const currentIndex=this.currentIndex,targetIndex=Math.max(0,Math.min(this._history.length-1,currentIndex+delta));if(targetIndex===currentIndex)return;const target=this._history[targetIndex];this._pendingGo=targetIndex,this.navigate(target).then(res=>{res.status!==NavigateStatus.success&&(this._pendingGo=null)})}initializeRouter(){this._history.push(this.currentRouteLocation)}pushHistory(data){this._updateHistory(data,!1)}replaceHistory(data){this._updateHistory(data,!0)}_updateHistory(data,isReplace){let newIndex;if(this._pendingGo!==null)this._history[this._pendingGo]=data,newIndex=this._pendingGo;else if(isReplace)this._history[this.currentIndex]=data,newIndex=this.currentIndex;else{const nextIndex=this.currentIndex+1;nextIndex<this._history.length?(this._history[nextIndex]=data,this._history.length=nextIndex+1,newIndex=nextIndex):(this._history.push(data),newIndex=this._history.length-1)}this._currentIndex=newIndex,this.completeNavigation(),this._pendingGo=null}}function defineRoutes(...routes){return routes}function defineRoute(route){return route}function createRouter(options){let router;return!(window!=null&&window.location)&&options.mode!=="memory"?(console.warn("当前环境非浏览器端,强制使用内存模式路由"),options.mode="memory",new RouterMemory(options).initialize()):(options.mode==="memory"?router=new RouterMemory(options):router=new RouterHistory(options),router.initialize())}function useRouter(){return RouterCore.instance}function useRoute(){return RouterCore.instance.currentRouteLocation}function lazy(lazyLoader){return Object.defineProperty(lazyLoader,LAZY_LOADER_SYMBOL,{value:!0}),lazyLoader}const INDEX_SYMBOL=Symbol("RouterViewCounter");class RouterView extends vitarx.Widget{constructor(props){super(props);__publicField(this,"_$index");__publicField(this,"_$currentRoute");__publicField(this,"_$currentElement",vitarx.shallowRef());const parentIndex=vitarx.inject(INDEX_SYMBOL,-1,this);this._$index=parentIndex+1,vitarx.provide(INDEX_SYMBOL,this._$index,this),this._$currentRoute=this.matchedRoute,this._$currentElement.value=RouterCore.routeViewElement(this._$currentRoute,this.name,this._$index),vitarx.watch(this.location.matched,(_c,o)=>{const newRoute=o[this.index];newRoute!==this._$currentRoute&&(this._$currentRoute=newRoute,this._$currentElement.value=RouterCore.routeViewElement(newRoute,this.name,this._$index))})}get index(){return this._$index}get isLastView(){return this.index===this.location.matched.length-1&&this.name==="default"}get name(){return this.props.name||"default"}get matchedRoute(){return this.location.matched[this.index]}get currentElement(){return this._$currentElement.value}get currentWidget(){var _a;return(_a=this._$currentElement.value)==null?void 0:_a.type}get location(){return RouterCore.instance.currentRouteLocation}onMounted(){this.completeViewRender()}onUpdated(){this.completeViewRender()}build(){return this.currentElement||vitarx.createElement(vitarx.Fragment)}completeViewRender(){this.isLastView&&RouterCore.instance._completeViewRender()}}class RouterLink extends vitarx.Widget{constructor(props){super(props);__publicField(this,"target");__publicField(this,"location");__publicField(this,"active");__publicField(this,"htmlProps");this.target=new vitarx.Computed(()=>{if(!this.props.to)return;const to=vitarx.isString(props.to)?{index:props.to}:props.to;return RouterLink.isHttpOrHttpsUrl(to.index)?to.index:to}),this.location=new vitarx.Computed(()=>{if(!this.target.value||typeof this.target.value=="string")return;const location=RouterCore.instance.createRouteLocation(this.target.value);return location.matched.length||console.warn(`[Vitarx.RouterLink][WARN]:索引:${this.target.value.index},未匹配到任何有效的路由线路,请检查to属性是否配置正确!`),location}),props.active!==void 0&&props.active!=="none"&&(this.active=new vitarx.Computed(()=>!this.location.value||typeof this.target.value=="string"||!this.target.value?!1:this.target.value.index.startsWith("/")?props.active==="obscure"?this.location.value.path==="/"?RouterCore.instance.currentRouteLocation.path==="/":RouterCore.instance.currentRouteLocation.fullPath.startsWith(this.location.value.path):this.location.value.path===RouterCore.instance.currentRouteLocation.path:!!RouterCore.instance.currentRouteLocation.matched.find(route=>route.name===this.target.value.index))),this.htmlProps=new vitarx.Computed(()=>{var _a;const props2={href:this.href,onClick:e=>this.navigate(e),children:this.children??((_a=this.location.value)==null?void 0:_a.index),draggable:this.props.draggable??!1,"v-bind":[this.props,["to","children","href","disabled","active","callback","onClick","onclick","aria-current"]]};return this.isActive&&(props2["aria-current"]="page"),this.isDisabled&&(props2.disabled=!0),props2})}static isHttpOrHttpsUrl(url){return/^(https?):\/\/[^\s\/$.?#].\S*$/i.test(url)}get isActive(){var _a;return((_a=this.active)==null?void 0:_a.value)&&!this.isDisabled}get isDisabled(){return this.props.disabled??!1}get href(){var _a;return typeof this.target.value=="string"?this.target.value:((_a=this.location.value)==null?void 0:_a.fullPath)||"javascript:void(0)"}navigate(e){typeof this.target.value!="string"&&(e.preventDefault(),this.location.value&&!this.isDisabled&&RouterCore.instance.navigate(this.location.value).then(res=>{var _a;res.status!==NavigateStatus.success&&console.warn(`[Vitarx.RouterLink][WARN]:导航到索引:${(_a=this.target.value)==null?void 0:_a.index}失败,${res.message}`),this.props.callback&&this.props.callback(res)}))}build(){return vitarx.createElement("a",this.htmlProps.value)}}exports2.HistoryRouter=RouterHistory,exports2.MemoryRouter=RouterMemory,exports2.NavigateStatus=NavigateStatus,exports2.Router=RouterCore,exports2.RouterLink=RouterLink,exports2.RouterView=RouterView,exports2.createRouter=createRouter,exports2.defineRoute=defineRoute,exports2.defineRoutes=defineRoutes,exports2.lazy=lazy,exports2.useRoute=useRoute,exports2.useRouter=useRouter,Object.defineProperty(exports2,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
(function(global,factory){typeof exports=="object"&&typeof module<"u"?factory(exports,require("vitarx")):typeof define=="function"&&define.amd?define(["exports","vitarx"],factory):(global=typeof globalThis<"u"?globalThis:global||self,factory(global.VitarxRouter={},global.Vitarx))})(this,function(exports2,vitarx){"use strict";var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value}):obj[key]=value;var __publicField=(obj,key,value)=>__defNormalProp(obj,typeof key!="symbol"?key+"":key,value);var NavigateStatus=(NavigateStatus2=>(NavigateStatus2[NavigateStatus2.success=0]="success",NavigateStatus2[NavigateStatus2.aborted=1]="aborted",NavigateStatus2[NavigateStatus2.cancelled=2]="cancelled",NavigateStatus2[NavigateStatus2.duplicated=3]="duplicated",NavigateStatus2[NavigateStatus2.not_matched=4]="not_matched",NavigateStatus2[NavigateStatus2.exception=5]="exception",NavigateStatus2))(NavigateStatus||{});function isVariablePath(path){return/\{[^}]+}/.test(path)}function optionalVariableCount(path){const pathWithoutSpaces=path.replace(/\s+/g,""),regex=/\{[\w-]+\?}/g,matches=pathWithoutSpaces.match(regex);return matches?matches.length:0}function isRouteGroup(route){return"children"in route&&route.children!==void 0&&route.children.length>0}function createDynamicPattern(path,pattern,strict,defaultPattern){let optional=0;const processVariable=(varName,isOptional)=>{const regex=pattern[varName];if(regex?regex instanceof RegExp||(console.warn(`[Vitarx.Router][WARN]:${path} 动态路径${varName}变量的自定义正则表达式必须是 RegExp 类型`),pattern[varName]=defaultPattern):pattern[varName]=defaultPattern,isOptional)return optional++,`(?:(${pattern[varName].source}))?`;if(optional)throw new Error(`[Vitarx.Router][ERROR]:动态路径 ${path} 中,可选变量 ${varName} 后不能存在任何必填变量`);return`(${pattern[varName].source})`},processedPath=path.replace(/{([^}?]+)\?}/g,(_,varName)=>processVariable(varName,!0)).replace(/{([^}]+)}/g,(_,varName)=>processVariable(varName,!1)).replace(/\//g,"\\/").replace(/\/?$/,"/?"),flags=strict?"":"i",segments=path.replace(/^\/|\/$/g,"").split("/").length;return{regex:new RegExp(`^${processedPath}$`,flags),length:segments,optional}}function formatPath(path){return path=`/${path}`.replace(/\s+/g,"").replace(/\/+/g,"/"),path.length?path==="/"||path==="/#/"?path:path.replace(/\/$/,""):"/"}function mergePathParams(path,params){if(!isVariablePath(path))return path;const oldPath=path;return path=path.replace(/{([^}]+)\?*}/g,(_match,paramName)=>{const isOptional=paramName.endsWith("?");if(isOptional&&(paramName=paramName.slice(0,-1)),params[paramName]===void 0){if(isOptional)return"";throw new TypeError(`[Vitarx.Router.mergePathParams] 访问路由${oldPath}时缺少参数:${paramName}`)}return String(params[paramName]).replace(/\s+/g,"_")}),formatPath(path)}function formatHash(hash,addHashPrefix){return typeof hash!="string"||!hash?"":(hash=hash.trim(),hash.startsWith("#")?hash:`#${hash}`)}function queryStringToObject(queryString){queryString=decodeURIComponent(queryString);const params=new URLSearchParams(queryString.startsWith("?")?queryString.substring(1):queryString),obj={};return params.forEach((value,key)=>obj[key]=value),obj}function objectToQueryString(obj){const queryString=new URLSearchParams(obj).toString();return queryString?`?${queryString}`:""}function urlToRouteTarget(url,mode,base){let path=decodeURIComponent(url.pathname),hash=decodeURIComponent(url.hash),query=queryStringToObject(url.search);if(path=formatPath(path.startsWith(base)?path.slice(base.length):path),mode==="hash"&&hash.includes("#/")){const hashPart=hash.slice(1),[fullPath,anchor]=hashPart.split("#");path=formatPath(fullPath||"/"),hash=anchor?`#${anchor}`:""}return{index:path,hash,query}}function splitPathAndSuffix(path){const suffix=getPathSuffix(path);return suffix&&(path=path.slice(0,-suffix.length)),{path,suffix:suffix.substring(1)}}function getPathSuffix(path){const lastDotIndex=path.lastIndexOf(".");return lastDotIndex!==-1&&lastDotIndex<path.length-1?`.${path.substring(lastDotIndex+1)}`:""}function isRouteLocationTypeObject(obj){if(typeof obj!="object"||obj===null)return!1;const keys=["index","fullPath","path","hash","params","query","matched","meta"];for(const key of keys)if(!Object.prototype.hasOwnProperty.call(obj,key))return!1;return!0}function validateSuffix(suffix,allowSuffix,inputPath,routePath){return inputPath==="/"||allowSuffix==="*"?!0:allowSuffix===!1?inputPath===routePath:Array.isArray(allowSuffix)?allowSuffix.includes(suffix):suffix===allowSuffix}function addPathSuffix(path,suffix){return suffix&&!path.endsWith("/")&&!path.includes(".")&&(path+=suffix.startsWith(".")?suffix:`.${suffix}`),path}function cloneRouteLocation(route){const{matched,...other}=route;return Object.assign(vitarx.deepClone(other),{matched:Array.from(matched)})}const validInjectProps=props=>["boolean","function"].includes(typeof props);function normalizeInjectProps(route){if(!route.widget)return;const injectProps={},inputValue=route.injectProps??!0,type=typeof inputValue;if(type==="object")for(const name in route.widget){const value=inputValue[name];if(value&&!validInjectProps(value))throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由线路配置,injectProps.${name}值错误,仅支持boolean、function类型`);injectProps[name]=value??!0}else if(type==="function"||type==="boolean")for(const name in route.widget)injectProps[name]=inputValue;else throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由线路配置,injectProps属性配置错误,仅支持boolean、{key:boolean|function}、function类型`);route.injectProps=injectProps}const validWidgetType=(widget,path)=>{const type=typeof widget;if(type!=="function"){if(type==="object"){if(!widget.default)throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${path} 路由配置,widget传入对象时则认为是命名视图,命名视图必须具有default视图`);for(const k in widget)validWidgetType(widget[k],path);return}throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${path} 路由配置,widget配置无效`)}};function normalizeRouteWidget(route){const isGroup=route.children.length;if(route.widget)validWidgetType(route.widget,route.path);else if(!isGroup)throw new TypeError(`[Vitarx.Router][ERROR]:请检查 ${route.path} 路由配置,widget和children不能同时为空。`);typeof route.widget=="function"&&(route.widget={default:route.widget})}function normalizeRoute(route,group,suffix){if(route.meta=route.meta||{},route.pattern=route.pattern||{},route.children=route.children||[],!Array.isArray(route.children))throw new TypeError(`[Vitarx.Router][TYPE_ERROR]:${route.path} 路由线路配置 children 类型错误,它必须是数组类型。`);if(!route.path.trim())throw new TypeError("[Vitarx.Router][TYPE_ERROR]:路由线路配置 path 不能为空");return route.path=formatPath(group?`${group.path}/${route.path}`:route.path),normalizeRouteWidget(route),normalizeInjectProps(route),route.suffix??(route.suffix=(group==null?void 0:group.suffix)??suffix),route.afterEnter??(route.afterEnter=group==null?void 0:group.afterEnter),route.beforeEnter??(route.beforeEnter=group==null?void 0:group.beforeEnter),route}class RouterRegistry{constructor(options){__publicField(this,"_options");__publicField(this,"_namedRoutes",new Map);__publicField(this,"_dynamicRoutes",new Map);__publicField(this,"_pathRoutes",new Map);__publicField(this,"_parentRoute",new WeakMap);const config={base:"/",strict:!1,mode:"path",scrollBehavior:"auto",anchorsScrollBehavior:"auto",suffix:"*",pattern:/[\w.]+/,defaultSuffix:"",...options};if(config.base=`/${config.base.replace(/^\/+|\/+$/g,"")}`,typeof config.suffix=="string"?config.suffix=config.suffix.replace(/\./g,""):Array.isArray(config.suffix)&&(config.suffix=config.suffix.map(item=>item.replace(/\./g,""))),config.defaultSuffix&&(config.defaultSuffix=config.defaultSuffix.replace(/\./g,"")),config.missing&&typeof config.missing!="function")throw new TypeError("[VitarxRouter][ERROR]:missing配置无效");this._options=config}get options(){return this._options}get mode(){return this._options.mode}get missing(){return this._options.missing}get pathRoutes(){return this._pathRoutes}get namedRoutes(){return this._namedRoutes}get dynamicRoutes(){return this._dynamicRoutes}get routes(){return this._options.routes}get basePath(){return this._options.base}get suffix(){return this._options.suffix}removeRoute(index){const deleteRoute=this.findRoute(index);if(deleteRoute)return this._pathRoutes.delete(deleteRoute.path),deleteRoute.name&&this._namedRoutes.delete(deleteRoute.name),this.removeDynamicRoute(deleteRoute.path),this.removedFromRoutes(deleteRoute),deleteRoute}addRoute(route,parent){if(parent){const parentRoute=this.findRoute(parent);if(!parentRoute)throw new Error(`[Vitarx.Router.addRoute][ERROR]:父路由${parent}不存在`);parentRoute.children.includes(parentRoute)||parentRoute.children.push(this.registerRoute(route,parentRoute))}else this.registerRoute(route),this._options.routes.push(route)}findRoute(target){const isRouterTarget=typeof target=="object",index=isRouterTarget?target.index:target;if(typeof index!="string")throw new TypeError(`[Vitarx.Router.getRoute][ERROR]:路由索引${target}类型错误,必须给定字符串类型`);if(index.startsWith("/")){const matched=this.matchRoute(index);return matched?(matched.params&&isRouterTarget&&(target.params=Object.assign(target.params||{},matched.params)),matched.route):void 0}return this.findNamedRoute(index)}findPathRoute(path){return this._options.strict||(path=path.toLowerCase()),this._pathRoutes.get(path)}findNamedRoute(name){return this._namedRoutes.get(name)}findParentRoute(route){return this._parentRoute.get(route)}matchRoute(path){let formattedPath=formatPath(path);this._options.strict||(formattedPath=formattedPath.toLowerCase());try{const{path:shortPath,suffix}=splitPathAndSuffix(formattedPath),staticRoute=this._pathRoutes.get(shortPath);if(staticRoute&&validateSuffix(suffix,staticRoute.suffix,formattedPath,staticRoute.path))return{route:staticRoute,params:void 0};const segmentCount=shortPath.split("/").filter(Boolean).length,candidates=this._dynamicRoutes.get(segmentCount);if(candidates){const normalizedPath=`${shortPath}/`,regexCache=new Map;for(const{regex,route}of candidates){const match=(regexCache.get(regex.source)||regex).exec(normalizedPath);if(!match)continue;const params={},keys=Object.keys(route.pattern);for(let i=0;i<keys.length;i++)params[keys[i]]=match[i+1];if(validateSuffix(suffix,route.suffix,formattedPath,formattedPath))return{route,params}}}if(!suffix&&!shortPath.endsWith("/index")){const indexRoute=this._pathRoutes.get(`${shortPath==="/"?"":shortPath}/index`);if(indexRoute&&validateSuffix(suffix,indexRoute.suffix,formattedPath,indexRoute.path))return{route:indexRoute,params:void 0}}else if(shortPath==="/index"){const indexRoute=this._pathRoutes.get("/");if(indexRoute&&validateSuffix(suffix,indexRoute.suffix,formattedPath,indexRoute.path))return{route:indexRoute,params:void 0}}}catch(error){console.error("Error in matchRoute:",error);return}}setupRoutes(routes){for(const route of routes)this.registerRoute(route)}removedFromRoutes(route){const parent=this.findParentRoute(route);if(parent!=null&&parent.children){const index2=parent.children.indexOf(route);index2!==-1&&parent.children.splice(index2,1)}const index=this._options.routes.indexOf(route);index!==-1&&this._options.routes.splice(index,1)}removeDynamicRoute(path){if(!isVariablePath(path))return;const length=path.split("/").filter(Boolean).length,removeRouteFromRecords=key=>{const records=this._dynamicRoutes.get(key);if(records){for(let i=0;i<records.length;i++)if(records[i].route.path===path){records.splice(i,1);break}}};removeRouteFromRecords(length);const count=optionalVariableCount(path);if(count>0)for(let i=1;i<=count;i++)removeRouteFromRecords(length-i)}registerRoute(route,group){const normalizedRoute=normalizeRoute(route,group,this.suffix);if(group&&this._parentRoute.set(normalizedRoute,group),isRouteGroup(normalizedRoute)){this.recordRoute(normalizedRoute);for(const child of normalizedRoute.children)this.registerRoute(child,normalizedRoute);normalizedRoute.redirect||(normalizedRoute.redirect=function(to){var _a;let first=normalizedRoute.children[0];for(;first;){if(first.redirect)return typeof first.redirect=="function"?first.redirect.call(this,to):first.redirect;if(first.widget)return{index:first.path};first=(_a=first.children)==null?void 0:_a[0]}console.error(`[Vitarx.Router][ERROR]:${normalizedRoute.path} 分组路由在没有配置重定向的情况下,它的第一个子路由必须具有widget或redirect,否则无法匹配视图`,normalizedRoute)})}else this.recordRoute(normalizedRoute);return normalizedRoute}strictPath(path){return this._options.strict?path:path.toLowerCase()}recordRoute(route){if(route.name){if(route.name.startsWith("/")&&(route.name=route.name.replace(/^\//,""),console.warn(`[Vitarx.Router][WARN]:命名路由(name)不要以/开头: ${route.name},因为内部需要使用/区分path、name`)),this._namedRoutes.has(route.name))throw new Error(`[Vitarx.Router][ERROR]:检测到重复的路由名称(name): ${route.name}`);this._namedRoutes.set(route.name,route)}const path=this.strictPath(route.path);if(this._pathRoutes.has(path))throw new Error(`[Vitarx.Router][ERROR]:检测到重复的路由路径(path): ${route.path}`);this._pathRoutes.set(path,route),isVariablePath(route.path)&&this.recordDynamicRoute(route)}recordDynamicRoute(route){const{regex,length,optional}=createDynamicPattern(route.path,route.pattern,this.options.strict,this.options.pattern),addToLengthMap=len=>{this._dynamicRoutes.has(len)||this._dynamicRoutes.set(len,[]),this._dynamicRoutes.get(len).push({regex,route})};if(addToLengthMap(length),optional>0)for(let i=1;i<=optional;i++)addToLengthMap(length-i)}}const __stringValueKeys=["path","hash","index","fullPath"];function patchUpdate(location,newLocation){diffUpdateArrays(location.matched,newLocation.matched),diffUpdateObjects(location.params,newLocation.params),diffUpdateObjects(location.query,newLocation.query),diffUpdateObjects(location.meta,newLocation.meta);for(const key of __stringValueKeys)location[key]!==newLocation[key]&&(location[key]=newLocation[key])}function diffUpdateArrays(a,b){for(let i=0;i<b.length;i++)a[i]!==b[i]&&(a[i]=b[i]);a.length>b.length&&(a.length=b.length)}function diffUpdateObjects(a,b){const aKeys=new Set(Object.keys(a)),bKeys=Object.keys(b);for(const key of bKeys)a[key]=b[key],aKeys.delete(key);for(const key of aKeys)delete a[key]}function diffUpdateProps(oldProps,newProps){const removeKeys=new Set(Object.keys(oldProps)),bKeys=Object.keys(newProps),changes=[];for(const key of bKeys)key!=="key"&&(oldProps[key]!==newProps[key]&&(oldProps[key]=newProps[key],changes.push(key)),removeKeys.delete(key));changes.push(...removeKeys);for(const key of removeKeys)delete oldProps[key];return changes}const _RouterCore=class _RouterCore extends RouterRegistry{constructor(options){if(_RouterCore._instance)throw new Error("[Vitarx.Router.constructor]:路由器实例已存在,不能创建多个实例");super(options);__publicField(this,"_currentTaskId",null);__publicField(this,"_taskCounter",0);__publicField(this,"_pendingReplace",null);__publicField(this,"_pendingPush",null);__publicField(this,"_scrollBehaviorHandler");__publicField(this,"_currentRouteLocation");__publicField(this,"_readonlyRouteLocation");__publicField(this,"_isBrowser",typeof window<"u"&&typeof window.document<"u");__publicField(this,"_scrollBehavior","auto");this._currentRouteLocation=vitarx.reactive({index:this._options.base,path:this._options.base,hash:"",fullPath:"",params:{},query:{},matched:vitarx.shallowReactive([]),meta:vitarx.markRaw({}),suffix:""}),this._readonlyRouteLocation=vitarx.readonly(this._currentRouteLocation)}static get instance(){if(!_RouterCore._instance)throw new Error("[Vitarx.Router.instance]:路由器实例未初始化");return _RouterCore._instance}get initialized(){return _RouterCore._instance!==void 0}get isBrowser(){return this._isBrowser}get scrollBehavior(){return this._scrollBehavior}get options(){return this._options}get mode(){return this._options.mode}get currentRouteLocation(){return this._readonlyRouteLocation}get isPendingNavigation(){return!!(this._pendingReplace||this._pendingPush)}get pendingReplaceData(){return this._pendingReplace}get pendingPushData(){return this._pendingPush}static routeViewElement(route,name,index){return route?this.instance.createRouteViewElement(route,name):index===0&&name==="default"&&this.instance.missing&&!this.instance.isPendingNavigation?vitarx.createElement(this.instance.missing):void 0}back(){return this.go(-1)}forward(){return this.go(1)}replace(target){return typeof target=="string"?this.navigate({index:target,isReplace:!0}):(target.isReplace=!0,this.navigate(target))}push(target){return typeof target=="string"?this.navigate({index:target,isReplace:!1}):(target.isReplace=!1,this.navigate(target))}initialize(){return _RouterCore._instance?this:(this.setupRoutes(this._options.routes),typeof this.options.scrollBehavior=="function"?this._scrollBehaviorHandler=this.options.scrollBehavior:this._scrollBehavior=this.options.scrollBehavior,this.initializeRouter(),_RouterCore._instance=this,this)}scrollTo(scrollTarget){if(!(!this.isBrowser||!scrollTarget||typeof scrollTarget!="object"))try{if("el"in scrollTarget){const{el,...rest}=scrollTarget,element=typeof el=="string"?document.querySelector(el):el;element&&element instanceof Element&&(element.scrollIntoView?element.scrollIntoView({behavior:this.scrollBehavior,...rest}):window.scrollTo({behavior:this.scrollBehavior,top:element.getBoundingClientRect().top+window.scrollY,left:element.getBoundingClientRect().left+window.scrollX}));return}window.scrollTo({behavior:this.scrollBehavior,...scrollTarget})}catch(e){console.error("[Vitarx.Router.scrollTo][WARN]:滚动到指定位置时发生错误,请检查滚动目标参数是否正确",e)}}createRouteLocation(target){if(isRouteLocationTypeObject(target))return target;const route=this.findRoute(target),{index,query={},params={},hash=""}=target,path=route?mergePathParams(route.path,params):formatPath(index),matched=[];if(route){let parent=this.findParentRoute(route);for(;parent;)parent.widget&&matched.unshift(parent),parent=this.findParentRoute(parent);matched.push(route)}const meta=route!=null&&route.meta?vitarx.deepClone(route.meta):{},hashStr=formatHash(hash),suffix=getPathSuffix(index),fullPath=this.makeFullPath(path,query,hashStr,suffix);return{index,path,hash:hashStr,fullPath,params,query,matched,meta,suffix}}navigate(target){const taskId=++this._taskCounter;this._currentTaskId=taskId;const isCurrentTask=()=>this._currentTaskId===taskId,from=cloneRouteLocation(this.currentRouteLocation),performNavigation=async(_target,isRedirect)=>{const createNavigateResult=(overrides={})=>({from,to,status:NavigateStatus.success,message:"导航成功",redirectFrom:isRedirect?target:void 0,...overrides}),to=this.createRouteLocation(_target);if(to.fullPath===this.currentRouteLocation.fullPath&&vitarx.isDeepEqual(to.params,this.currentRouteLocation.params))return createNavigateResult({status:NavigateStatus.duplicated,message:"导航到相同的路由,被系统阻止!"});if(to.matched.at(-1)===this._currentRouteLocation.matched.at(-1)&&to.path===this._currentRouteLocation.path&&vitarx.isDeepEqual(to.query,this._currentRouteLocation.query)&&to.hash!==this._currentRouteLocation.hash)return this.updateHash(to.hash),createNavigateResult({status:NavigateStatus.success,message:"仅hash变化,导航成功!"});const matched=to.matched.at(-1);if(matched!=null&&matched.redirect){let redirectTarget;if(typeof matched.redirect=="object"&&matched.redirect.index)redirectTarget=matched.redirect;else if(typeof matched.redirect=="string")redirectTarget={index:matched.redirect};else if(typeof matched.redirect=="function"){const redirectHandleResult=matched.redirect.call(this,to);vitarx.isObject(redirectHandleResult)&&(redirectTarget=redirectHandleResult)}if(redirectTarget!=null&&redirectTarget.index)return performNavigation(redirectTarget,!0)}try{const result=await this.onBeforeEach(to,from);return result===!1?createNavigateResult({status:NavigateStatus.aborted,message:`导航到${to.index}目标时被前置守卫钩子阻止!`}):isCurrentTask()?typeof result=="object"&&result.index!==_target.index?(result.isReplace??(result.isReplace=!1),performNavigation(result,!0)):typeof result=="string"&&result!==_target.index?performNavigation({index:result,isReplace:!1},!0):!to.matched.length&&!this.missing?createNavigateResult({status:NavigateStatus.not_matched,message:`未匹配到任何路由规则,被系统阻止!请检测目标索引(${to.index})是否已注册路由。`}):("isReplace"in _target&&_target.isReplace?(this._pendingReplace=to,this.replaceHistory(to)):(this._pendingPush=to,this.pushHistory(to)),createNavigateResult(!to.matched.length&&this.missing?{status:NavigateStatus.not_matched,message:`${to.fullPath}未匹配到任何路由规则,被系统允许访问,因为系统定义了missing视图。`}:void 0)):createNavigateResult({status:NavigateStatus.cancelled,message:"已被新的导航请求替代,取消此次导航!"})}catch(error){return console.error("[Vitarx.Router.navigate][ERROR]:导航时捕获到了异常",error),createNavigateResult({status:NavigateStatus.exception,message:"导航时捕获到了异常",error})}};return performNavigation(target,!1)}updateQuery(query){vitarx.isDeepEqual(this._currentRouteLocation.query,query)||(this._currentRouteLocation.query=query,this._currentRouteLocation.fullPath=this.makeFullPath(this._currentRouteLocation.path,query,this._currentRouteLocation.hash,this._currentRouteLocation.suffix))}updateHash(hash){if(typeof hash!="string")throw new TypeError(`[Vitarx.Router.updateHash][WARN]:hash值只能是字符串类型,给定${hash}`);const newHash=formatHash(hash);newHash!==this._currentRouteLocation.hash&&(this._currentRouteLocation.hash=newHash,this._currentRouteLocation.fullPath=this.makeFullPath(this._currentRouteLocation.path,this._currentRouteLocation.query,newHash,this._currentRouteLocation.suffix))}createViewProps(route,name){var _a;const injectProps=(_a=route.injectProps)==null?void 0:_a[name];let props;return injectProps===!0?props=JSON.parse(JSON.stringify(this._currentRouteLocation.params)):injectProps===!1?props={}:typeof injectProps=="function"?props=injectProps(this.currentRouteLocation):vitarx.isRecordObject(injectProps)?props=injectProps:props={},props}createRouteViewElement(route,name){var _a;const widget=(_a=route.widget)==null?void 0:_a[name];if(!widget)return;const props=this.createViewProps(route,name);return props.key=route.path,vitarx.createElement(widget,props)}_completeViewRender(){}completeNavigation(savedPosition){const newData=this.pendingReplaceData||this.pendingPushData;if(!newData)throw new Error("[Vitarx.Router.completeNavigation][ERROR]:没有处于等待状态的导航请求。");const from=cloneRouteLocation(this.currentRouteLocation);this._completeViewRender=()=>{this.onScrollBehavior(this.currentRouteLocation,from,savedPosition).then(),this.onAfterEach(this.currentRouteLocation,from)},this.updateRouteLocation(newData),this._pendingReplace=null,this._pendingPush=null}makeFullPath(path,query,hash,suffix){return hash&&!hash.startsWith("#")&&(hash=`#${hash}`),typeof query=="object"&&(query=objectToQueryString(query)),path=addPathSuffix(path,suffix||this._options.defaultSuffix),this.mode==="hash"?formatPath(`${this.basePath}/${query}#${path}${hash}`):formatPath(`${this.basePath}${path}${query}${hash}`)}onBeforeEach(to,from){var _a;const matched=to.matched.at(-1);return matched&&"beforeEnter"in matched&&typeof matched.beforeEnter=="function"?matched.beforeEnter.call(this,to,from):(_a=this._options.beforeEach)==null?void 0:_a.call(this,to,from)}onAfterEach(to,from){var _a;const matched=to.matched.at(-1);if(matched)return"afterEnter"in matched&&typeof matched.afterEnter=="function"?matched.afterEnter.call(this,to,from):(_a=this._options.afterEach)==null?void 0:_a.call(this,to,from)}updateRouteLocation(newLocation){patchUpdate(this._currentRouteLocation,newLocation)}async onScrollBehavior(to,from,savedPosition){try{if(this._scrollBehaviorHandler){const scrollTarget=await this._scrollBehaviorHandler(to,from,savedPosition);scrollTarget&&this.scrollTo(scrollTarget)}else!savedPosition&&!to.hash?this.scrollTo({left:0,top:0}):this.scrollTo(savedPosition??{el:to.hash})}catch(e){console.error("[Vitarx.Router.onScrollBehavior]['ERROR']:处理滚动行为时捕获到了异常",e)}}removeRoute(index){const removed=super.removeRoute(index);if(removed){const index2=this._currentRouteLocation.matched.indexOf(removed);index2!==-1&&this._currentRouteLocation.matched.splice(index2,1)}return removed}install(app){app.register("router",this)}};__publicField(_RouterCore,"_instance");let RouterCore=_RouterCore;class RouterHistory extends RouterCore{constructor(options){["path","hash"].includes(options.mode)||(options.mode="hash"),super(options),options.mode==="hash"&&this.ensureHash()}updateHash(hash){if(super.updateHash(hash),this.saveCurrentScrollPosition(),this.webHistory.pushState(this.createState(this.currentRouteLocation),"",this.currentRouteLocation.fullPath),!hash.trim())window.scrollTo(0,0);else{const anchorId=hash.startsWith("#")?hash.slice(1):hash;try{const element=window.document.getElementById(anchorId);element&&element.scrollIntoView({behavior:this.options.anchorsScrollBehavior})}catch(e){console.error(`滚动到目标锚点${hash}失败`,e)}}}updateQuery(query){super.updateQuery(query),this.webHistory.pushState(this.createState(this.currentRouteLocation),"",this.currentRouteLocation.fullPath)}get currentRouteTarget(){return urlToRouteTarget(window.location,this.mode,this.basePath)}get webHistory(){return window.history}go(delta){this.webHistory.go(delta)}initializeRouter(){window.addEventListener("popstate",this.onPopState.bind(this)),this.replace(this.currentRouteTarget).then(res=>{res.status!==NavigateStatus.success&&console.warn(`[VitarxRouter.initializeRouter]:路由初始化匹配失败,${res.message}`)})}pushHistory(data){this.saveCurrentScrollPosition(),this.webHistory.pushState(this.createState(data),"",data.fullPath),this.completeNavigation()}replaceHistory(data){var _a;const scrollPosition=(_a=this.webHistory.state)==null?void 0:_a.scrollPosition;this.webHistory.replaceState(this.createState(data),"",data.fullPath),this.completeNavigation(scrollPosition)}saveCurrentScrollPosition(){const scrollPosition={left:window.scrollX,top:window.scrollY,behavior:this.scrollBehavior};this.webHistory.replaceState({...this.webHistory.state,scrollPosition},"",window.location.href)}createState(data,hash,query){const{matched,...state}=data;return typeof hash=="string"&&(state.hash=hash),typeof query=="object"&&(state.query=query),JSON.parse(JSON.stringify(state))}onPopState(event){var _a;let newTarget;(_a=event.state)!=null&&_a.index?newTarget={index:event.state.index,hash:event.state.hash,query:event.state.query}:newTarget=this.currentRouteTarget,this.replace(newTarget).then(res=>{if(!res.redirectFrom&&this.mode==="hash"&&res.status!==NavigateStatus.success)if(res.status===NavigateStatus.not_matched){if(res.to.index.startsWith("/")){const anchorId=res.to.index.slice(1),element=window.document.getElementById(anchorId);element&&element.scrollIntoView({behavior:this.scrollBehavior}),this.updateHash(`#${anchorId}`)}}else res.status===NavigateStatus.duplicated&&this.webHistory.replaceState(this.createState(this.currentRouteLocation),"",this.currentRouteLocation.fullPath)})}ensureHash(){const{pathname,search,hash}=window.location;if(!hash){const path=`${this.basePath}${search}#${pathname}`;window.location.replace(path)}}}class RouterMemory extends RouterCore{constructor(options){super(options);__publicField(this,"_history",[]);__publicField(this,"_pendingGo",null);__publicField(this,"_currentIndex",0);options.mode="memory"}get currentIndex(){return this._currentIndex}go(delta){if(!delta)return;const currentIndex=this.currentIndex,targetIndex=Math.max(0,Math.min(this._history.length-1,currentIndex+delta));if(targetIndex===currentIndex)return;const target=this._history[targetIndex];this._pendingGo=targetIndex,this.navigate(target).then(res=>{res.status!==NavigateStatus.success&&(this._pendingGo=null)})}initializeRouter(){this._history.push(this.currentRouteLocation)}pushHistory(data){this._updateHistory(data,!1)}replaceHistory(data){this._updateHistory(data,!0)}_updateHistory(data,isReplace){let newIndex;if(this._pendingGo!==null)this._history[this._pendingGo]=data,newIndex=this._pendingGo;else if(isReplace)this._history[this.currentIndex]=data,newIndex=this.currentIndex;else{const nextIndex=this.currentIndex+1;nextIndex<this._history.length?(this._history[nextIndex]=data,this._history.length=nextIndex+1,newIndex=nextIndex):(this._history.push(data),newIndex=this._history.length-1)}this._currentIndex=newIndex,this.completeNavigation(),this._pendingGo=null}}function defineRoutes(...routes){return routes}function defineRoute(route){return route}function createRouter(options){let router;return typeof window>"u"&&options.mode!=="memory"?(console.warn("当前环境非浏览器端,强制使用内存模式路由"),options.mode="memory",new RouterMemory(options).initialize()):(options.mode==="memory"?router=new RouterMemory(options):router=new RouterHistory(options),router.initialize())}function useRouter(){return RouterCore.instance}function useRoute(){return RouterCore.instance.currentRouteLocation}const INDEX_SYMBOL=Symbol("RouterViewCounter");class RouterView extends vitarx.Widget{constructor(props){super(props);__publicField(this,"_$index");__publicField(this,"_$currentRoute");__publicField(this,"_$currentElement",vitarx.shallowRef());const parentIndex=vitarx.inject(INDEX_SYMBOL,-1,this);this._$index=parentIndex+1,vitarx.provide(INDEX_SYMBOL,this._$index,this),this._$currentRoute=this.matchedRoute,this._$currentElement.value=RouterCore.routeViewElement(this._$currentRoute,this.name,this._$index);const router=useRouter();let paramStr=JSON.stringify(this.location.params);vitarx.watch(this.location.matched,(_c,o)=>{const newRoute=o[this.index];newRoute!==this._$currentRoute&&(this._$currentRoute=newRoute,this._$currentElement.value=RouterCore.routeViewElement(newRoute,this.name,this._$index))}),vitarx.watch(this.location.params,()=>{if(!this._$currentRoute)return;const newParams=JSON.stringify(this.location.params);if(newParams!==paramStr){paramStr=newParams;const newProps=router.createViewProps(this._$currentRoute,this.name),oldProps=vitarx.toRaw(this._$currentElement.value.props),changes=diffUpdateProps(oldProps,newProps);changes.length>0&&vitarx.Observers.trigger(this._$currentElement.value.props,changes)}})}get index(){return this._$index}get isLastView(){return this.index===this.location.matched.length-1&&this.name==="default"}get name(){return this.props.name||"default"}get matchedRoute(){return this.location.matched[this.index]}get currentElement(){return this._$currentElement.value}get currentWidget(){var _a;return(_a=this._$currentElement.value)==null?void 0:_a.type}get location(){return RouterCore.instance.currentRouteLocation}onMounted(){this.completeViewRender()}onUpdated(){this.completeViewRender()}build(){return this.currentElement||vitarx.createElement(vitarx.Fragment)}completeViewRender(){this.isLastView&&RouterCore.instance._completeViewRender()}}class RouterLink extends vitarx.Widget{constructor(props){super(props);__publicField(this,"target");__publicField(this,"location");__publicField(this,"active");__publicField(this,"htmlProps");this.target=new vitarx.Computed(()=>{if(!this.props.to)return;const to=vitarx.isString(props.to)?{index:decodeURIComponent(props.to)}:props.to;if(RouterLink.isHttpOrHttpsUrl(to.index)||to.index.startsWith("#"))return to.index;if(to.index.includes("#")){const[index,hash]=to.index.split("#",2);to.index=index,to.hash=`#${hash}`}return to}),this.location=new vitarx.Computed(()=>{if(!this.target.value||typeof this.target.value=="string")return;const location=RouterCore.instance.createRouteLocation(this.target.value);return location.matched.length||console.warn(`[Vitarx.RouterLink][WARN]:索引:${this.target.value.index},未匹配到任何有效的路由线路,请检查to属性是否配置正确!`),location}),props.active!==void 0&&props.active!=="none"&&(this.active=new vitarx.Computed(()=>!this.location.value||typeof this.target.value=="string"||!this.target.value?!1:this.target.value.index.startsWith("/")?props.active==="obscure"?this.location.value.path==="/"?RouterCore.instance.currentRouteLocation.path==="/":RouterCore.instance.currentRouteLocation.fullPath.startsWith(this.location.value.path):this.location.value.path===RouterCore.instance.currentRouteLocation.path:!!RouterCore.instance.currentRouteLocation.matched.find(route=>route.name===this.target.value.index))),this.htmlProps=new vitarx.Computed(()=>{var _a;const props2={href:this.href,onClick:e=>this.navigate(e),children:this.children??((_a=this.location.value)==null?void 0:_a.index),draggable:this.props.draggable??!1,"v-bind":[this.props,["to","children","href","disabled","active","callback","onClick","onclick","aria-current"]]};return this.isActive&&(props2["aria-current"]="page"),this.isDisabled&&(props2.disabled=!0),props2})}static isHttpOrHttpsUrl(url){return/^(https?):\/\/[^\s\/$.?#].\S*$/i.test(url)}get isActive(){var _a;return((_a=this.active)==null?void 0:_a.value)&&!this.isDisabled}get isDisabled(){return this.props.disabled??!1}get href(){var _a;return typeof this.target.value=="string"?this.target.value:((_a=this.location.value)==null?void 0:_a.fullPath)||"javascript:void(0)"}navigate(e){typeof this.target.value!="string"&&(e.preventDefault(),this.location.value&&!this.isDisabled&&RouterCore.instance.navigate(this.location.value).then(res=>{var _a;res.status!==NavigateStatus.success&&console.warn(`[Vitarx.RouterLink][WARN]:导航到索引:${(_a=this.target.value)==null?void 0:_a.index}失败,${res.message}`),this.props.callback&&this.props.callback(res)}))}build(){return vitarx.createElement("a",this.htmlProps.value)}}exports2.HistoryRouter=RouterHistory,exports2.MemoryRouter=RouterMemory,exports2.NavigateStatus=NavigateStatus,exports2.Router=RouterCore,exports2.RouterLink=RouterLink,exports2.RouterView=RouterView,exports2.createRouter=createRouter,exports2.defineRoute=defineRoute,exports2.defineRoutes=defineRoutes,exports2.useRoute=useRoute,exports2.useRouter=useRouter,Object.defineProperty(exports2,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitarx-router",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "Vitarx前端框架的配套路由器",
|
|
5
|
+
"author": "ZhuChongLin <8210856@qq.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/vitarx-lib/router"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://vitarx.cn/router",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"vitarx",
|
|
14
|
+
"router",
|
|
15
|
+
"vitarx-router",
|
|
16
|
+
"vitarx-router-lib"
|
|
17
|
+
],
|
|
5
18
|
"type": "module",
|
|
6
|
-
"main": "dist/vitarx-router.umd.js",
|
|
7
|
-
"module": "dist/vitarx-router.es.js",
|
|
8
|
-
"types": "dist/vitarx-router.d.ts",
|
|
19
|
+
"main": "./dist/vitarx-router.umd.js",
|
|
20
|
+
"module": "./dist/vitarx-router.es.js",
|
|
21
|
+
"types": "./dist/vitarx-router.d.ts",
|
|
22
|
+
"unpkg": "./dist/vitarx-router.umd.js",
|
|
23
|
+
"jsdelivr": "./dist/vitarx-router.umd.js",
|
|
9
24
|
"exports": {
|
|
10
25
|
".": {
|
|
11
26
|
"require": {
|
|
@@ -30,7 +45,7 @@
|
|
|
30
45
|
"push": "npm publish --access=public"
|
|
31
46
|
},
|
|
32
47
|
"peerDependencies": {
|
|
33
|
-
"vitarx": "^
|
|
48
|
+
"vitarx": "^2.0.0-beta.1"
|
|
34
49
|
},
|
|
35
50
|
"devDependencies": {
|
|
36
51
|
"@types/node": "^22.10.0",
|