vitarx-router 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 朱冲林
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # VitarxRouter
2
+
3
+ ________________________________________________________________________
4
+
5
+ ## 安装
6
+
7
+ ```shell
8
+ npm install vitarx-router
9
+ ```
10
+
11
+ ## 使用
12
+ ```javascript
13
+ import { Router } from 'vitarx-router'
14
+ ```
@@ -0,0 +1,2 @@
1
+ export * from './widget/index.js';
2
+ export * from './router/index.js';
@@ -0,0 +1,92 @@
1
+ import { LazyLoad, Route, RouteLocation, RouterOptions } from './type.js';
2
+ import { default as Router } from './router.js';
3
+ import { default as WebHistoryRouter } from './web-history-router.js';
4
+ import { default as MemoryRouter } from './memory-router.js';
5
+ import { LazyLoader, Reactive, WidgetType } from 'vitarx';
6
+ /**
7
+ * 定义路由表
8
+ *
9
+ * 使用defineRoutes定义路由表可以获得更好的代码提示。
10
+ *
11
+ * @param {Route[]} routes - 路由配置表
12
+ */
13
+ export declare function defineRoutes(...routes: Route[]): Route[];
14
+ /**
15
+ * 定义路由
16
+ *
17
+ * 使用defineRoute定义路由可以获得更好的代码提示。
18
+ *
19
+ * @param {Route} route - 路由配置
20
+ */
21
+ export declare function defineRoute(route: Route): Route;
22
+ /**
23
+ * 创建内存路由器
24
+ *
25
+ * 不支持浏览器的前进后退等操作,只能通过实例中的方法来管理路由。
26
+ *
27
+ * 你必须调用一次`router.replace(target)`方法来跳转到目标路由。
28
+ *
29
+ * @param {RouterOptions} options - 配置
30
+ * @return {MemoryRouter} - 内存路由器实例
31
+ */
32
+ export declare function createRouter(options: RouterOptions & {
33
+ mode: 'memory';
34
+ }): MemoryRouter;
35
+ /**
36
+ * 创建History路由器
37
+ *
38
+ * 基于History API实现路由功能,支持浏览器前进后退、刷新等操作。
39
+ *
40
+ * 它与`MemoryRouter`路由不同的是,它不需要初始化过后调用`router.replace(target)`方法来替换默认路由,
41
+ * 内部会自动根据`window.location`去匹配路由。
42
+ *
43
+ * @param {RouterOptions} options - 路由配置
44
+ * @return {WebHistoryRouter} - HistoryRouter实例
45
+ */
46
+ export declare function createRouter(options: RouterOptions | (RouterOptions & {
47
+ mode: 'path' | 'hash';
48
+ })): WebHistoryRouter;
49
+ /**
50
+ * 获取路由器实例
51
+ *
52
+ * 与使用`Router.instance`静态属性获取是一致的效果。
53
+ *
54
+ * @return {Router} - 路由器实例
55
+ * @throws {Error} - 如果路由器实例未初始化,则抛出异常
56
+ */
57
+ export declare function useRouter<T extends Router>(): T;
58
+ /**
59
+ * 获取当前`RouteLocation`对象
60
+ *
61
+ * 它是`Router.instance.currentRouteLocation`属性的助手函数,优化函数式编程体验。
62
+ *
63
+ * 简单示例:
64
+ * ```jsx
65
+ * import { useRoute } from 'vitarx-router'
66
+ *
67
+ * // 监听路由参数变化示例
68
+ * export default function App() {
69
+ * const route = useRoute()
70
+ * watch(()=>route.params.id, (newId, oldId) => {
71
+ * console.log(`监听到id参数变化, 旧值:${oldId}, 新值:${newId}`)
72
+ * })
73
+ * return <div>当前路由参数ID:{route.params.id}</div>
74
+ * }
75
+ * ```
76
+ *
77
+ * @return {Readonly<Reactive<RouteLocation>>} - 只读的`RouteLocation`对象
78
+ */
79
+ export declare function useRoute(): Readonly<Reactive<RouteLocation>>;
80
+ /**
81
+ * ## 标记延迟加载
82
+ *
83
+ * 由于直接定义`() => import('./xxx.js')`会导致类型与函数式组件冲突,
84
+ * 在未执行函数之前难以有效判断其类型,所以这里使用Symbol标记懒加载器。
85
+ *
86
+ * @example
87
+ * lazy(() => import('./xxx.js'))
88
+ *
89
+ * @template T - WidgetType
90
+ * @param {LazyLoader<T>} lazyLoader - 函数返回的import即是惰性加载器
91
+ */
92
+ export declare function lazy<T extends WidgetType>(lazyLoader: LazyLoader<T>): LazyLoad<T>;
@@ -0,0 +1,6 @@
1
+ export type * from './type.js';
2
+ export { NavigateStatus } from './type.js';
3
+ export { default as Router } from './router.js';
4
+ export { default as WebHistoryRouter } from './web-history-router.js';
5
+ export { default as MemoryRouter } from './memory-router.js';
6
+ export * from './helper.js';
@@ -0,0 +1,45 @@
1
+ import { default as Router } from './router.js';
2
+ import { RouteLocation, RouterOptions } from './type.js';
3
+ /**
4
+ * 基于内存实现的路由器
5
+ *
6
+ * 仅支持路由器操作前进、后退、跳转等操作
7
+ *
8
+ * > 注意:不要在浏览器端使用,因为浏览器端有原生的history对象,使用内存模式会和浏览器端的历史记录冲突,导致路由异常。
9
+ */
10
+ export default class MemoryRouter extends Router {
11
+ protected _history: RouteLocation[];
12
+ protected _pendingGo: number | null;
13
+ constructor(options: RouterOptions<'memory'>);
14
+ private _currentIndex;
15
+ /**
16
+ * 当前历史路由索引
17
+ *
18
+ * @protected
19
+ */
20
+ protected get currentIndex(): number;
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ go(delta?: number): void;
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ protected initializeRouter(): void;
29
+ /**
30
+ * 添加历史记录
31
+ */
32
+ protected pushHistory(data: RouteLocation): void;
33
+ /**
34
+ * 替换历史记录
35
+ */
36
+ protected replaceHistory(data: RouteLocation): void;
37
+ /**
38
+ * 更新历史记录
39
+ *
40
+ * @param {RouteLocation} data - 目标路由
41
+ * @param {boolean} isReplace - 是否为替换操作
42
+ * @private
43
+ */
44
+ private _updateHistory;
45
+ }
@@ -0,0 +1,414 @@
1
+ import { _ScrollBehavior, _ScrollToOptions, BeforeEachCallbackResult, DynamicRouteRecord, HashStr, HistoryMode, InitializedRouterOptions, MatchResult, NavigateResult, Route, RouteIndex, RouteLocation, RouteName, RouteNormalized, RoutePath, RouterOptions, RouteTarget, ScrollTarget } from './type.js';
2
+ import { Reactive, VNode, WidgetType } from 'vitarx';
3
+ /**
4
+ * 路由器基类
5
+ */
6
+ export default abstract class Router {
7
+ #private;
8
+ private readonly _options;
9
+ private readonly _namedRoutes;
10
+ private readonly _dynamicRoutes;
11
+ private readonly _pathRoutes;
12
+ private readonly _parentRoute;
13
+ private _currentTaskId;
14
+ private _taskCounter;
15
+ /**
16
+ * 是否正在执行 replace 操作
17
+ *
18
+ * @private
19
+ */
20
+ private _pendingReplace;
21
+ /**
22
+ * 是否正在执行 push 操作
23
+ *
24
+ * @private
25
+ */
26
+ private _pendingPush;
27
+ private _scrollBehaviorHandler;
28
+ private readonly _currentRouteLocation;
29
+ protected constructor(options: RouterOptions);
30
+ /**
31
+ * 获取单例实例
32
+ *
33
+ * @return {Router} - 路由器实例
34
+ */
35
+ static get instance(): Router;
36
+ private _isBrowser;
37
+ /**
38
+ * 是否运行在浏览器端
39
+ */
40
+ get isBrowser(): boolean;
41
+ private _scrollBehavior;
42
+ /**
43
+ * 滚动行为
44
+ *
45
+ * 如果options.scrollBehavior为函数,则scrollBehavior固定为auto'。
46
+ */
47
+ get scrollBehavior(): _ScrollBehavior;
48
+ /**
49
+ * 获取配置
50
+ *
51
+ * @return {Readonly<InitializedRouterOptions>} - 初始化配置
52
+ */
53
+ get options(): Readonly<InitializedRouterOptions>;
54
+ /**
55
+ * 路由器模式
56
+ */
57
+ get mode(): HistoryMode;
58
+ /**
59
+ * 获取所有路由映射
60
+ *
61
+ * map键值是path,值是路由对象
62
+ *
63
+ * @return {Map<string, Route>}
64
+ */
65
+ get pathRoutes(): ReadonlyMap<string, Route>;
66
+ /**
67
+ * 获取所有命名路由映射
68
+ *
69
+ * map键值是name,值是路由对象
70
+ *
71
+ * @return {Map<string, Route>}
72
+ */
73
+ get namedRoutes(): ReadonlyMap<string, Route>;
74
+ /**
75
+ * 动态路由记录
76
+ *
77
+ * map键值是path段长度,值是Array<{regex: RegExp,route:RouteNormalized}>
78
+ */
79
+ get dynamicRoutes(): Map<number, DynamicRouteRecord[]>;
80
+ /**
81
+ * ## 获取已规范的路由表
82
+ *
83
+ * 它的内存地址始终指向的是初始化时传入的路由表,但它的数据结构是经过内部规范化处理过的。
84
+ *
85
+ * 所有嵌套`Route`的path都会被自动补全为完整的路径
86
+ *
87
+ * > 注意:不要尝试修改已规范化的路由表!请使用`removeRoute`和`addRoute`方法实现路由的变更。
88
+ *
89
+ * @return {RouteNormalized[]}
90
+ */
91
+ get routes(): ReadonlyArray<RouteNormalized>;
92
+ /**
93
+ * 基本路径
94
+ */
95
+ get basePath(): `/${string}`;
96
+ /**
97
+ * 判断路由器是否初始化完成
98
+ *
99
+ * @return {boolean} - 如果初始化完成,返回true,否则返回false
100
+ */
101
+ get initialized(): boolean;
102
+ /**
103
+ * 受支持的`path`后缀名
104
+ */
105
+ get suffix(): Exclude<RouterOptions['suffix'], void>;
106
+ /**
107
+ * 当前路由数据
108
+ *
109
+ * 它是只读的,不要在外部修改它!
110
+ *
111
+ * @return {Readonly<RouteLocation>} - 当前路由数据
112
+ */
113
+ get currentRouteLocation(): Readonly<Reactive<RouteLocation>>;
114
+ /**
115
+ * 是否处于等待状态
116
+ *
117
+ * @protected
118
+ */
119
+ protected get isPendingNavigation(): boolean;
120
+ /**
121
+ * 等待替换完成的数据
122
+ *
123
+ * @protected
124
+ */
125
+ protected get pendingReplaceData(): RouteLocation | null;
126
+ /**
127
+ * 等待跳转完成的数据
128
+ *
129
+ * @protected
130
+ */
131
+ protected get pendingPushData(): RouteLocation | null;
132
+ /**
133
+ * 路由视图
134
+ *
135
+ * 内部方法,用于获取路由线路对应的视图元素虚拟节点。
136
+ *
137
+ * @internal
138
+ * @param {RouteNormalized} route - 路由对象
139
+ * @param {string} name - 视图名称
140
+ * @return {VNode<WidgetType> | undefined} - 视图元素虚拟节点
141
+ */
142
+ static routeView(route: RouteNormalized, name: string): VNode<WidgetType> | undefined;
143
+ /**
144
+ * 跳转指定的历史记录位置
145
+ *
146
+ * 如果未向该函数传参或delta相等于 0,则该函数与调用location.reload()具有相同的效果。
147
+ *
148
+ * @param {number} delta - 跳转的步数(正数为前进,负数为后退)
149
+ */
150
+ abstract go(delta?: number): void;
151
+ /**
152
+ * 后退到上一个历史记录
153
+ */
154
+ back(): void;
155
+ /**
156
+ * 前进到下一个历史记录
157
+ */
158
+ forward(): void;
159
+ /**
160
+ * 替换当前页面
161
+ *
162
+ * @param {RouteTarget} target - 目标
163
+ * @return {boolean} - 是否跳转成功,非内存模式始终返回true
164
+ */
165
+ replace(target: RouteTarget | RouteIndex): Promise<NavigateResult>;
166
+ /**
167
+ * 跳转到新的页面
168
+ *
169
+ * @param {RouteTarget} target - 目标
170
+ * @return {boolean} - 是否跳转成功,非内存模式始终返回true
171
+ */
172
+ push(target: RouteTarget | RouteIndex): Promise<NavigateResult>;
173
+ /**
174
+ * 删除路由
175
+ *
176
+ * @param {string} index 路由索引,如果/开头则匹配path,其他匹配name
177
+ * @param {boolean} isRemoveFromRoutes 是否从路由表中移除,内部递归时传递的参数,无需外部传入!
178
+ */
179
+ removeRoute(index: RouteIndex, isRemoveFromRoutes?: boolean): void;
180
+ /**
181
+ * 添加路由
182
+ *
183
+ * @param {Route} route - 路由描述对象
184
+ * @param {string} parent - 父路由的path或name,不传入则添加至路由表根节点
185
+ */
186
+ addRoute(route: Route, parent?: string): void;
187
+ /**
188
+ * 超找路由
189
+ *
190
+ * 传入的是`path`则会调用`matchRoute`方法,传入的是`name`则会调用`getNamedRoute`方法
191
+ *
192
+ * @param {RouteIndex|RouteTarget} target - 路由索引,如果index以/开头则匹配path,其他匹配name
193
+ * @return {RouteNormalized | undefined} - 路由对象,如果不存在则返回undefined
194
+ */
195
+ findRoute(target: RouteIndex | RouteTarget): RouteNormalized | undefined;
196
+ /**
197
+ * 查找命名路由
198
+ *
199
+ * @param {string} name - 路由名称
200
+ */
201
+ findNamedRoute(name: RouteName): RouteNormalized | undefined;
202
+ /**
203
+ * 初始化路由器
204
+ *
205
+ * 只能初始化一次,多次初始化无效。
206
+ *
207
+ * 如果你使用的`createRouter(options)`助手函数创建的路由器实例则无需调用该方法。
208
+ *
209
+ * @return {this} - 返回当前路由器实例
210
+ */
211
+ initialize(): this;
212
+ /**
213
+ * 此方法用于浏览器端滚动到指定位置
214
+ *
215
+ * @param scrollTarget
216
+ * @protected
217
+ */
218
+ scrollTo(scrollTarget: ScrollTarget | undefined): void;
219
+ /**
220
+ * 获取路由的父路由
221
+ *
222
+ * @param route - 路由对象
223
+ * @return {RouteNormalized | undefined}
224
+ */
225
+ findParentRoute(route: RouteNormalized): RouteNormalized | undefined;
226
+ /**
227
+ * 该方法提供给`RouterView`完成渲染时调用
228
+ *
229
+ * @internal
230
+ */
231
+ protected _completeViewRender(): void;
232
+ /**
233
+ * 完成导航
234
+ *
235
+ * 所有子类在完成导航的后续处理过后必须调用该方法!
236
+ *
237
+ * @param {RouteLocation} data - 如果是由`replace`或`push`方法发起的导航则无需传入此参数。
238
+ * @param {_ScrollToOptions} savedPosition - 保存的滚动位置信息,用于恢复滚动位置
239
+ * @protected
240
+ */
241
+ protected completeNavigation(data?: RouteLocation, savedPosition?: _ScrollToOptions): void;
242
+ /**
243
+ * 更新当前导航数据中的query参数
244
+ *
245
+ * 会同步更新`fullPath`
246
+ *
247
+ * @param {Record<string, string>} query - 新的query参数对象
248
+ * @protected
249
+ */
250
+ protected updateQuery(query: Record<string, string>): void;
251
+ /**
252
+ * 更新当前导航数据中的hash参数
253
+ *
254
+ * 会同步更新`fullPath`
255
+ *
256
+ * @param {`#${string}` | ''} hash - 新的hash参数,如果为空则表示无hash
257
+ * @protected
258
+ */
259
+ protected updateHash(hash: `#${string}` | ''): void;
260
+ /**
261
+ * 初始化路由器
262
+ *
263
+ * 子类可重写此方法以完成路由器的初始化
264
+ *
265
+ * @private
266
+ */
267
+ protected abstract initializeRouter(): void;
268
+ /**
269
+ * 路由匹配
270
+ *
271
+ *
272
+ * @param {string} path - 路径
273
+ *
274
+ * @return {MatchResult} 如果匹配失败则返回undefined
275
+ */
276
+ protected matchRoute(path: RoutePath): MatchResult;
277
+ /**
278
+ * 创建完整路径
279
+ *
280
+ * @protected
281
+ * @param path - 路径
282
+ * @param query - ?查询参数
283
+ * @param hash - #哈希值
284
+ */
285
+ protected makeFullPath(path: string, query: `?${string}` | '' | Record<string, string>, hash: HashStr): `${string}`;
286
+ /**
287
+ * 添加历史记录
288
+ *
289
+ * 子类必须实现该方法
290
+ *
291
+ * @param data
292
+ * @protected
293
+ */
294
+ protected abstract pushHistory(data: RouteLocation): void;
295
+ /**
296
+ * 替换历史记录
297
+ *
298
+ * 子类必须实现该方法
299
+ *
300
+ * @param {RouteLocation} data - 目标路由
301
+ * @protected
302
+ */
303
+ protected abstract replaceHistory(data: RouteLocation): void;
304
+ /**
305
+ * 判断是否相同的导航请求
306
+ *
307
+ * @param to
308
+ * @param from
309
+ * @protected
310
+ */
311
+ protected isSameNavigate(to: RouteLocation, from: RouteLocation): boolean;
312
+ /**
313
+ * 根据路由目标创建导航数据
314
+ *
315
+ * @param {RouteTarget} target - 路由目标
316
+ * @protected
317
+ */
318
+ protected createRouteLocation(target: RouteTarget): RouteLocation;
319
+ /**
320
+ * 触发路由前置守卫
321
+ *
322
+ * @param {DeepReadonly<RouteLocation>} to - 路由目标对象
323
+ * @param {DeepReadonly<RouteLocation>} from - 前路由对象
324
+ * @return {false | RouteTarget} - 返回false表示阻止导航,返回新的路由目标对象则表示导航到新的目标
325
+ */
326
+ protected onBeforeEach(to: DeepReadonly<RouteLocation>, from: DeepReadonly<RouteLocation>): BeforeEachCallbackResult;
327
+ /**
328
+ * 触发路由后置守卫
329
+ *
330
+ * @param {DeepReadonly<RouteLocation>} to - 路由目标对象
331
+ * @param {DeepReadonly<RouteLocation>} from - 前路由对象
332
+ */
333
+ protected onAfterEach(to: DeepReadonly<RouteLocation>, from: DeepReadonly<RouteLocation>): void;
334
+ /**
335
+ * 路由跳转的通用方法
336
+ *
337
+ * 仅供内部使用,外部请使用`push`|`replace`方法
338
+ *
339
+ * @protected
340
+ * @param {RouteTarget} target - 目标
341
+ * @return {Promise<NavigateResult>} - 是否导航成功
342
+ */
343
+ protected navigate(target: RouteTarget): Promise<NavigateResult>;
344
+ /**
345
+ * 更新路由数据
346
+ *
347
+ * @private
348
+ * @param {RouteLocation} newLocation - 新的路由数据对象
349
+ */
350
+ private updateRouteLocation;
351
+ /**
352
+ * 判断是否为允许的后缀
353
+ *
354
+ * @param suffix
355
+ * @private
356
+ */
357
+ private isAllowedSuffix;
358
+ /**
359
+ * 触发滚动行为
360
+ *
361
+ * @param {RouteLocation} to - 目标导航数据对象
362
+ * @param {RouteLocation} from - 前导航数据对象
363
+ * @param {_ScrollToOptions | undefined} savedPosition - 保存的滚动位置
364
+ * @private
365
+ */
366
+ private onScrollBehavior;
367
+ /**
368
+ * 从源路由表中删除路由
369
+ *
370
+ * @param route
371
+ * @protected
372
+ */
373
+ private removedFromRoutes;
374
+ /**
375
+ * 删除动态路由映射
376
+ *
377
+ * @param path
378
+ * @protected
379
+ */
380
+ private removeDynamicRoute;
381
+ /**
382
+ * 初始化路由表
383
+ *
384
+ * @param routes
385
+ */
386
+ private setupRoutes;
387
+ /**
388
+ * 注册路由
389
+ *
390
+ * @param {Route} route - 路由对象
391
+ * @param {RouteNormalized} group - 路由所在的分组
392
+ * @protected
393
+ */
394
+ private registerRoute;
395
+ /**
396
+ * 如果路径是严格匹配,则转换为小写
397
+ *
398
+ * @param path
399
+ * @private
400
+ */
401
+ private strictPath;
402
+ /**
403
+ * 记录路由
404
+ *
405
+ * @param {Route} route - 路由对象
406
+ * @protected
407
+ */
408
+ private recordRoute;
409
+ /**
410
+ * 添加动态路由
411
+ * @param route
412
+ */
413
+ private recordDynamicRoute;
414
+ }