vitarx-router 4.0.0-beta.21 → 4.0.0-beta.22

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.
@@ -71,7 +71,7 @@ export function parseHashContent(hashContent) {
71
71
  // 同样限制分割数为 2,防止路径中包含 ?
72
72
  const [pathname, queryString] = pathAndQuery.split('?', 2);
73
73
  // 3. 格式化路径
74
- path = normalizePath(pathname || '/');
74
+ path = pathname ? normalizePath(pathname) : '/';
75
75
  // 4. 解析查询参数
76
76
  if (queryString) {
77
77
  query = parseQuery(queryString);
@@ -97,7 +97,7 @@ export function mergePathVariable(path, params) {
97
97
  return String(value).replace(/\s+/g, '_');
98
98
  });
99
99
  // 使用 formatPath 处理可能出现的双斜杠 (如 /user//) 或首尾斜杠问题
100
- return normalizePath(fullPath);
100
+ return normalizePath(fullPath, true);
101
101
  }
102
102
  /**
103
103
  * 合并两个路由模式对象
@@ -9,7 +9,9 @@ export interface RouteManagerOptions {
9
9
  /**
10
10
  * 是否启用严格模式
11
11
  *
12
- * 启用后路径匹配将严格匹配,即路径末尾的斜杠会被视为重要字符
12
+ * 启用后路径匹配将严格匹配即 /user/ 结尾存在斜杠会匹配失败
13
+ *
14
+ * 禁用后路径匹配将宽匹配即 /user/ 会匹配 /user
13
15
  *
14
16
  * @default false
15
17
  */
@@ -23,8 +25,8 @@ export interface RouteManagerOptions {
23
25
  /**
24
26
  * 是否启用索引回退匹配。
25
27
  *
26
- * 开启后,当访问深层路径(如 /user/index)未匹配时,
27
- * 会尝试移除最后一段路径(/user)再次进行匹配。
28
+ * 开启后,当访问深层index路径(如 /user/index)未匹配时,
29
+ * 会尝试移除index路径(/user)再次进行匹配。
28
30
  * 匹配成功则渲染组件,且保持 URL 不变。
29
31
  *
30
32
  * 仅支持匹配静态路径,不做动态路径匹配!
@@ -126,7 +128,7 @@ export declare class RouteManager {
126
128
  * @returns 规范化后的路径(根据 ignoreCase 配置转换为小写)
127
129
  * @private
128
130
  */
129
- private normalizePath;
131
+ private toLookupPath;
130
132
  /**
131
133
  * 根据路径查找路由
132
134
  *
@@ -127,7 +127,7 @@ export class RouteManager {
127
127
  * @returns 规范化后的路径(根据 ignoreCase 配置转换为小写)
128
128
  * @private
129
129
  */
130
- normalizePath(path) {
130
+ toLookupPath(path) {
131
131
  return this.config.ignoreCase ? path.toLowerCase() : path;
132
132
  }
133
133
  /**
@@ -137,7 +137,7 @@ export class RouteManager {
137
137
  * @returns 路由记录对象,如果未找到则返回 undefined
138
138
  */
139
139
  findByPath(path) {
140
- const normalizedPath = this.normalizePath(path);
140
+ const normalizedPath = this.toLookupPath(path);
141
141
  return this.staticRoutes.get(normalizedPath) ?? this.aliasRoutes.get(normalizedPath) ?? null;
142
142
  }
143
143
  /**
@@ -183,9 +183,7 @@ export class RouteManager {
183
183
  normalizedPath = normalizedPath.slice(0, -1);
184
184
  }
185
185
  // 1. 标准化路径
186
- const lookupPath = this.config.ignoreCase
187
- ? normalizedPath.toLowerCase()
188
- : normalizedPath;
186
+ const lookupPath = this.toLookupPath(normalizedPath);
189
187
  // 2. 静态路由精确匹配
190
188
  const staticRoute = this.staticRoutes.get(lookupPath);
191
189
  if (staticRoute) {
@@ -197,14 +195,14 @@ export class RouteManager {
197
195
  return { path, route: aliasRoute, params: {} };
198
196
  }
199
197
  // 3. 动态路由匹配
200
- const pathSegments = normalizedPath.split('/').filter(Boolean);
198
+ const pathSegments = path.split('/').filter(Boolean);
201
199
  const segmentCount = pathSegments.length;
202
200
  const dynamicCandidates = this.dynamicRoutes.get(segmentCount);
203
201
  if (dynamicCandidates) {
204
202
  for (const { regex, route } of dynamicCandidates) {
205
203
  regex.lastIndex = 0;
206
204
  // 执行正则匹配
207
- const regexResult = regex.exec(normalizedPath);
205
+ const regexResult = regex.exec(path);
208
206
  if (regexResult) {
209
207
  const params = {};
210
208
  // 解析捕获组参数
@@ -222,6 +220,7 @@ export class RouteManager {
222
220
  }
223
221
  }
224
222
  }
223
+ // 4. 结尾斜杠索引回退匹配
225
224
  if (lookupPath.endsWith('/index') && this.config.fallbackIndex) {
226
225
  const fallbackPath = lookupPath.slice(0, -6);
227
226
  const fallbackRoute = this.staticRoutes.get(fallbackPath || '/');
@@ -354,14 +353,14 @@ export class RouteManager {
354
353
  // 删除当前路由
355
354
  this.routes.delete(route);
356
355
  // 删除静态路由映射
357
- this.staticRoutes.delete(this.normalizePath(route.path));
356
+ this.staticRoutes.delete(this.toLookupPath(route.path));
358
357
  // 删除命名路由映射
359
358
  if (route.name)
360
359
  this.namedRoutes.delete(route.name);
361
360
  // 删除别名映射
362
361
  if (route.aliases) {
363
362
  for (const alias of route.aliases) {
364
- this.aliasRoutes.delete(this.normalizePath(alias));
363
+ this.aliasRoutes.delete(this.toLookupPath(alias));
365
364
  // 如果别名是动态路由,也需要从 dynamicRoutes 中移除
366
365
  if (isVariablePath(alias)) {
367
366
  this.removeAliasDynamicRoute(alias, route);
@@ -462,7 +461,7 @@ export class RouteManager {
462
461
  record.parent = parent;
463
462
  }
464
463
  const rawPath = route.path.trim();
465
- record.path = normalizePath(parent ? `${parent.path}/${rawPath}` : `/${rawPath}`);
464
+ record.path = normalizePath(parent ? `${parent.path}/${rawPath}` : `/${rawPath}`, true);
466
465
  if (route.name) {
467
466
  record.name = route.name;
468
467
  }
@@ -513,13 +512,13 @@ export class RouteManager {
513
512
  aliasPath = parent.path;
514
513
  }
515
514
  else if (rawAlias.startsWith('/')) {
516
- aliasPath = normalizePath(rawAlias);
515
+ aliasPath = normalizePath(rawAlias, true);
517
516
  }
518
517
  else if (!parent) {
519
- aliasPath = normalizePath(`/${rawAlias}`);
518
+ aliasPath = normalizePath(`/${rawAlias}`, true);
520
519
  }
521
520
  else {
522
- aliasPath = normalizePath(`${parent.path}/${rawAlias}`);
521
+ aliasPath = normalizePath(`${parent.path}/${rawAlias}`, true);
523
522
  }
524
523
  record.aliases.push(aliasPath);
525
524
  }
@@ -552,7 +551,7 @@ export class RouteManager {
552
551
  this.registerDynamicRoute(route, route.fullPattern);
553
552
  }
554
553
  else {
555
- const path = this.normalizePath(route.path);
554
+ const path = this.toLookupPath(route.path);
556
555
  // 检查是否已存在相同路径的路由
557
556
  if (this.staticRoutes.has(path)) {
558
557
  // 抛出错误:检测到重复的路由路径
@@ -564,7 +563,6 @@ export class RouteManager {
564
563
  // 注册别名
565
564
  if (route.aliases && route.aliases.length > 0) {
566
565
  for (const alias of route.aliases) {
567
- const aliasPath = this.normalizePath(alias);
568
566
  if (isVariablePath(alias)) {
569
567
  if (!validateAliasVariables(route.pattern, alias)) {
570
568
  throw new Error(`[Router] Alias "${alias}" variables do not match route "${route.path}" pattern`);
@@ -573,6 +571,7 @@ export class RouteManager {
573
571
  this.registerDynamicRoute(route, regex, alias);
574
572
  }
575
573
  else {
574
+ const aliasPath = this.toLookupPath(alias);
576
575
  if (route.pattern && route.pattern.length > 0) {
577
576
  throw new Error(`[Router] Alias "${alias}" for dynamic route "${route.path}" must contain variables`);
578
577
  }
@@ -965,7 +965,7 @@ export class Router {
965
965
  const queryStr = stringifyQuery(query);
966
966
  const href = `${path}${queryStr}${hashStr}`;
967
967
  return this.config.mode === 'hash'
968
- ? normalizePath(`${this.config.base}/#${href}`, true)
968
+ ? normalizePath(`${this.config.base}#${href}`)
969
969
  : normalizePath(`${this.config.base}${href}`);
970
970
  }
971
971
  /**
@@ -130,7 +130,7 @@ export function useLink(props) {
130
130
  const matchedRoute = route.value;
131
131
  if (!matchedRoute)
132
132
  return false;
133
- return router.route.href === matchedRoute.href;
133
+ return router.route.path === matchedRoute.path;
134
134
  });
135
135
  /**
136
136
  * 导航处理函数
@@ -17,19 +17,20 @@ export declare function stringifyQuery(obj: Record<string, string>): `?${string}
17
17
  /**
18
18
  * 归一化path
19
19
  *
20
- * 去除所有空格、替换重复的斜杠、去除尾部的斜杠
20
+ * 去除所有空格、替换重复的斜杠
21
21
  *
22
22
  * @example
23
23
  * normalizePath('/ foo') // '/foo'
24
- * normalizePath('/foo/') // '/foo'
24
+ * normalizePath('/foo/') // '/foo/'
25
25
  * normalizePath('/foo/bar') // '/foo/bar'
26
26
  * normalizePath('foo/') // '/foo'
27
+ * normalizePath('/foo/',true) // '/foo'
27
28
  *
28
29
  * @param {string} path - 路径字符串
29
- * @param [hashMode = false] - 是否为hash模式,如果是则兼容`/#/`
30
+ * @param {boolean} removeEndSlash - 是否去除尾随斜杠
30
31
  * @return {string} - 格式化后的路径字符串
31
32
  */
32
- export declare function normalizePath(path: string, hashMode?: boolean): `/${string}`;
33
+ export declare function normalizePath(path: string, removeEndSlash?: boolean): `/${string}`;
33
34
  /**
34
35
  * 克隆路由位置对象
35
36
  *
@@ -28,24 +28,27 @@ export function stringifyQuery(obj) {
28
28
  /**
29
29
  * 归一化path
30
30
  *
31
- * 去除所有空格、替换重复的斜杠、去除尾部的斜杠
31
+ * 去除所有空格、替换重复的斜杠
32
32
  *
33
33
  * @example
34
34
  * normalizePath('/ foo') // '/foo'
35
- * normalizePath('/foo/') // '/foo'
35
+ * normalizePath('/foo/') // '/foo/'
36
36
  * normalizePath('/foo/bar') // '/foo/bar'
37
37
  * normalizePath('foo/') // '/foo'
38
+ * normalizePath('/foo/',true) // '/foo'
38
39
  *
39
40
  * @param {string} path - 路径字符串
40
- * @param [hashMode = false] - 是否为hash模式,如果是则兼容`/#/`
41
+ * @param {boolean} removeEndSlash - 是否去除尾随斜杠
41
42
  * @return {string} - 格式化后的路径字符串
42
43
  */
43
- export function normalizePath(path, hashMode = false) {
44
+ export function normalizePath(path, removeEndSlash = false) {
44
45
  // 去除所有空格 处理重复//
45
- const formated = `/${path.trim()}`.replace(/\s+/g, '').replace(/\/+/g, '/');
46
- if (formated === '/' || (hashMode && formated === '/#/'))
47
- return formated;
48
- return formated.replace(/\/$/, '');
46
+ let normalizedPath = `/${path.trim()}`.replace(/\s+/g, '').replace(/\/+/g, '/');
47
+ if (removeEndSlash && normalizedPath.endsWith('/') && normalizedPath !== '/') {
48
+ normalizedPath = normalizedPath.replace(/\/$/, '');
49
+ }
50
+ // 去除尾随斜杠
51
+ return normalizedPath;
49
52
  }
50
53
  /**
51
54
  * 克隆路由位置对象
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitarx-router",
3
- "version": "4.0.0-beta.21",
3
+ "version": "4.0.0-beta.22",
4
4
  "description": "Official routing solution for Vitarx framework with declarative routing, navigation guards, dynamic routes, file-based routing with HMR, and full TypeScript support.",
5
5
  "author": "ZhuChonglin <8210856@qq.com>",
6
6
  "license": "MIT",