vitarx-router 4.0.0-beta.25 → 4.0.0-beta.26

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.
@@ -1,9 +1,11 @@
1
1
  import { createView, isFunction, isPlainObject, logger } from 'vitarx';
2
+ import { isExternalLink } from '../core/index.js';
2
3
  import { useLink } from '../core/shared/index.js';
3
4
  const EXTRA_PROPS = [
4
5
  'to',
5
6
  'replace',
6
7
  'viewTransition',
8
+ 'exactMatchMode',
7
9
  'disabled',
8
10
  'callback',
9
11
  'onclick',
@@ -41,7 +43,7 @@ const EXTRA_PROPS = [
41
43
  export function RouterLink(props, location) {
42
44
  const link = useLink(props);
43
45
  if (__VITARX_DEV__) {
44
- if (!link.route.value) {
46
+ if (!link.route.value && !isExternalLink(link.href.value)) {
45
47
  logger.warn(`[RouterLink] No match found for to: ${isPlainObject(props.to) ? JSON.stringify(props.to) : String(props.to)}`, location);
46
48
  }
47
49
  }
@@ -49,7 +49,7 @@ export function isNavIndex(val) {
49
49
  */
50
50
  export function hasOnlyChangeHash(route1, route2) {
51
51
  return (route1.hash !== route2.hash &&
52
- route1.path === route2.path &&
52
+ route1.matched.at(-1) === route2.matched.at(-1) &&
53
53
  isDeepEqual(route1.query, route2.query));
54
54
  }
55
55
  /**
@@ -590,6 +590,8 @@ export class Router {
590
590
  if (!context.to)
591
591
  return null;
592
592
  if (hasOnlyChangeHash(context.to, context.from)) {
593
+ // 更新路由位置的 hash 值并触发 hashUpdate 回调
594
+ this._routeLocation.hash = context.to.hash;
593
595
  this._routeLocation.href = context.to.href;
594
596
  this.hashUpdate?.(context.to);
595
597
  context.result.state = NavState.success;
@@ -140,9 +140,14 @@ export class WebRouter extends Router {
140
140
  if ('el' in target && target.el) {
141
141
  const { el, ...rest } = target;
142
142
  if (isString(el)) {
143
- // 对选择器字符串进行转义
144
- const escapedSelector = el.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1');
145
- const element = document.querySelector(escapedSelector);
143
+ let element = null;
144
+ try {
145
+ element = document.querySelector(el);
146
+ }
147
+ catch (e) {
148
+ logger.warn(`[Router] Invalid selector "${el}", skipping scroll to element`, e);
149
+ return;
150
+ }
146
151
  if (element) {
147
152
  if (element.scrollIntoView) {
148
153
  element.scrollIntoView(rest);
@@ -1,7 +1,7 @@
1
1
  import { type Computed } from 'vitarx';
2
- import type { NavigateResult, NavTarget, RouteIndex, RouteLocation, RoutePath, URLHash } from '../types/index.js';
3
- export type HTTPUrl = `http://${string}` | `https://${string}`;
4
- type LinkToTarget<T extends RouteIndex = RouteIndex> = NavTarget<T> | T | `${RoutePath}?${string}` | URLHash | HTTPUrl;
2
+ import type { NavigateResult, NavTarget, RouteIndex, RouteLocation } from '../types/index.js';
3
+ type LinkToTarget<T extends RouteIndex = RouteIndex> = NavTarget<T> | T | string;
4
+ export type LinkExactMatchMode = 'path' | 'href' | 'hash' | 'query';
5
5
  export interface UseLinkOptions<T extends RouteIndex = RouteIndex> {
6
6
  /**
7
7
  * 要跳转的目标
@@ -23,6 +23,17 @@ export interface UseLinkOptions<T extends RouteIndex = RouteIndex> {
23
23
  * @default false
24
24
  */
25
25
  viewTransition?: boolean;
26
+ /**
27
+ * 精确匹配模式
28
+ *
29
+ * - 'path':精确匹配路径
30
+ * - 'href':精确匹配完整链接
31
+ * - 'hash':精确匹配路径和锚点
32
+ * - 'query':精确匹配路径和查询参数
33
+ *
34
+ * @default 'path'
35
+ */
36
+ exactMatchMode?: LinkExactMatchMode;
26
37
  }
27
38
  export interface UseLinkReturn {
28
39
  /**
@@ -88,6 +99,7 @@ export declare function isPathExactMatch(currentPath: string, targetPath: string
88
99
  * @param props.to - 要跳转的目标,可以是路由目标对象、路由索引、带查询参数的路径字符串、哈希值或 HTTP/HTTPS 链接。
89
100
  * @param [props.replace] - 是否使用 `router.replace()` 而不是 `router.push()`。优先级低于 `to.replace`。默认为 `false`。
90
101
  * @param [props.viewTransition] - 如果支持则使用 `document.startViewTransition()` 进行视图过渡。默认为 `false`。
102
+ * @param [props.exactMatchMode] - 精确匹配模式。可选值有:'path'、'href'、'hash'、'query'。默认为 'path'。
91
103
  * @returns 返回一个包含链接属性和导航方法的对象。
92
104
  * @returns {Computed<string>} returns.href - 链接的 `href` 属性值。
93
105
  * @returns {Computed<RouteLocation | null>} returns.route - 匹配的路由信息,如果未匹配则返回 `null`。
@@ -2,7 +2,7 @@ import { computed, isPlainObject, isString } from 'vitarx';
2
2
  import { NavState } from '../common/constant.js';
3
3
  import { isExternalLink, isNavTarget, isRoutePath } from '../common/utils.js';
4
4
  import { useRouter } from './inject.js';
5
- import { cloneRouteLocation, parseQuery, removeTrailingSlash } from './utils.js';
5
+ import { cloneRouteLocation, parseQuery, removeTrailingSlash, stringifyQuery } from './utils.js';
6
6
  /**
7
7
  * 处理视图转换
8
8
  * @param callback
@@ -63,6 +63,7 @@ export function isPathExactMatch(currentPath, targetPath) {
63
63
  * @param props.to - 要跳转的目标,可以是路由目标对象、路由索引、带查询参数的路径字符串、哈希值或 HTTP/HTTPS 链接。
64
64
  * @param [props.replace] - 是否使用 `router.replace()` 而不是 `router.push()`。优先级低于 `to.replace`。默认为 `false`。
65
65
  * @param [props.viewTransition] - 如果支持则使用 `document.startViewTransition()` 进行视图过渡。默认为 `false`。
66
+ * @param [props.exactMatchMode] - 精确匹配模式。可选值有:'path'、'href'、'hash'、'query'。默认为 'path'。
66
67
  * @returns 返回一个包含链接属性和导航方法的对象。
67
68
  * @returns {Computed<string>} returns.href - 链接的 `href` 属性值。
68
69
  * @returns {Computed<RouteLocation | null>} returns.route - 匹配的路由信息,如果未匹配则返回 `null`。
@@ -95,7 +96,7 @@ export function useLink(props) {
95
96
  else {
96
97
  return null;
97
98
  }
98
- // 处理字符串目标
99
+ // 处理路由路径
99
100
  if (isString(target.index)) {
100
101
  // 兼容纯锚点连接跳转
101
102
  if (target.index.startsWith('#')) {
@@ -150,7 +151,20 @@ export function useLink(props) {
150
151
  const matchedRoute = route.value;
151
152
  if (!matchedRoute)
152
153
  return false;
153
- return isPathExactMatch(router.route.path, matchedRoute.path);
154
+ const isMatched = isPathExactMatch(router.route.path, matchedRoute.path);
155
+ if (!isMatched)
156
+ return false;
157
+ const mode = props.exactMatchMode || 'path';
158
+ switch (mode) {
159
+ case 'href':
160
+ return router.route.href === matchedRoute.href;
161
+ case 'hash':
162
+ return router.route.hash === matchedRoute.hash;
163
+ case 'query':
164
+ return stringifyQuery(router.route.query) === stringifyQuery(matchedRoute.query);
165
+ default:
166
+ return true;
167
+ }
154
168
  });
155
169
  /**
156
170
  * 导航处理函数
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitarx-router",
3
- "version": "4.0.0-beta.25",
3
+ "version": "4.0.0-beta.26",
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",
@@ -55,6 +55,9 @@
55
55
  "peerDependenciesMeta": {
56
56
  "vite": {
57
57
  "optional": true
58
+ },
59
+ "vitarx": {
60
+ "optional": true
58
61
  }
59
62
  },
60
63
  "dependencies": {