bobe-router 0.0.59 → 0.0.61
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/dist/bobe-router.cjs.js +317 -0
- package/dist/bobe-router.cjs.js.map +1 -0
- package/dist/bobe-router.esm.js +313 -0
- package/dist/bobe-router.esm.js.map +1 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.umd.js +321 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/plugin.cjs.js +241 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.esm.js +239 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/ssr-routes.d.ts +2 -0
- package/package.json +21 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bobe-router.esm.js","sources":["../src/match.ts","../src/global.ts","../src/router.ts"],"sourcesContent":["import type { RouteMap, MatchResult } from './type';\n\n/** 编译路径模式为正则:/post/:id → ^\\/post\\/([^/]+)$,捕获名为 id */\nfunction compilePattern(pattern: string): { regex: RegExp; params: string[] } {\n const paramNames: string[] = [];\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // 转义正则特殊字符\n .replace(/:([a-zA-Z_]\\w*)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n return {\n regex: new RegExp(`^${regexStr}$`),\n params: paramNames,\n };\n}\n\n/** 预编译所有路由的正则 */\ntype CompiledRoute = {\n pattern: string;\n regex: RegExp;\n paramNames: string[];\n};\n\nlet compiledCache: CompiledRoute[] | null = null;\nlet cacheKey: RouteMap | null = null;\n\nfunction compileRoutes(map: RouteMap): CompiledRoute[] {\n if (cacheKey === map && compiledCache) return compiledCache;\n compiledCache = Object.keys(map)\n .filter((k) => k !== '*') // 通配符最后匹配\n .map((pattern) => {\n const { regex, params: paramNames } = compilePattern(pattern);\n return { pattern, regex, paramNames };\n });\n // 通配符路由放最后\n if (map['*']) {\n compiledCache.push({ pattern: '*', regex: /.*/, paramNames: [] });\n }\n cacheKey = map;\n return compiledCache;\n}\n\n/**\n * 根据路径匹配路由表\n * @param path - URL 路径,如 /post/42\n * @param map - 路由表\n * @returns 匹配结果,含路径参数;null 表示 404\n */\nexport function match(path: string, map: RouteMap): MatchResult | null {\n // 确保以 / 开头\n const normalized = path.startsWith('/') ? path : `/${path}`;\n\n for (const compiled of compileRoutes(map)) {\n const m = normalized.match(compiled.regex);\n if (!m) continue;\n\n const params: Record<string, string> = {};\n for (let i = 0; i < compiled.paramNames.length; i++) {\n params[compiled.paramNames[i]] = m[i + 1];\n }\n return { path: compiled.pattern, params };\n }\n return null;\n}\n","/** 通过 globalThis 传递的路由全局变量名 */\nexport enum GlobalKey {\n /** 路由表:{ [url]: { import/component } } */\n Routes = '__BOBE_INIT_ROUTES__',\n /** 菜单树:Menu[] */\n Menus = '__BOBE_INIT_MENUS__',\n /** 初始路径 */\n Path = '__BOBE_INIT_PATH__',\n}\n","import { Store, StoreIgnoreKeys } from 'aoye';\nimport type { RouteMap, RouteEntry, RouteRecord, GuardResult, Menu, RouterOptions } from './type';\nimport { match } from './match';\nimport { GlobalKey } from './global';\n\n/** 创建带初始数据的 RouteRecord */\nexport function createRouteRecord(\n opts: Partial<Pick<RouteRecord, 'import' | 'component' | 'params'>> = {}\n): RouteRecord {\n return {\n import: opts.import,\n component: opts.component,\n status: opts.component ? 'loaded' : 'idle',\n params: opts.params,\n };\n}\n\nexport class Router extends Store {\n static [StoreIgnoreKeys] = ['routes', 'menus', 'stack', 'ready'] as string[];\n\n /** 当前激活的路由,模板用 {active.component} 渲染 */\n active: RouteEntry | null = null;\n\n /** 路由表 */\n routes: RouteMap = {};\n\n /** 目录嵌套菜单 */\n menus: Menu[] = [];\n\n /** 进入守卫 */\n enterGuard?: (to: RouteEntry) => GuardResult | Promise<GuardResult>;\n\n /** 离开守卫 */\n leaveGuard?: (from: RouteEntry) => GuardResult | Promise<GuardResult>;\n\n /** 历史栈(不响应式) */\n private stack: RouteEntry[] = [];\n private stackIndex = 0;\n\n /** 待预加载的路径集合 */\n private idleSet = new Set<string>();\n\n /** 最大并行预加载数 */\n maxPreload = 3;\n\n /** 当前正在加载的组件数 */\n private loadingCount = 0;\n\n /**\n * 注册首屏就绪回调。\n * - 已初始化 → 同步执行 cb\n * - 未初始化 → 入队,首屏加载完成后执行\n * 支持多次调用。\n * 无参数时返回 Promise。\n */\n ready(): Promise<void>;\n ready(cb: () => void): void;\n ready(cb?: () => void): Promise<void> | void {\n if (cb) {\n if (this.#inited) { cb(); }\n else { this.#readyQueue.push(cb); }\n return;\n }\n return new Promise<void>(resolve => this.ready(resolve));\n }\n\n #inited = false;\n #readyQueue: (() => void)[] = [];\n\n constructor(opt?: RouterOptions) {\n super();\n\n const routes = opt?.routes;\n const initialPath = opt?.initialPath;\n\n // 1. routes 优先级:用户传入 > SSR 注入 > 空\n this.routes = routes\n || (globalThis as any)[GlobalKey.Routes]\n || {};\n\n // 2. menus 优先级:SSR 注入 > 空\n const injectedMenus = (globalThis as any)[GlobalKey.Menus];\n if (injectedMenus) this.menus = injectedMenus;\n\n // 3. path 优先级:用户传入 > SSR 注入 > location > '/'\n const path = initialPath\n || (globalThis as any)[GlobalKey.Path]\n || (typeof location !== 'undefined' ? location.pathname : '/');\n\n this.#init(path);\n }\n\n // ====== 初始化 ======\n async #init(path: string): Promise<void> {\n\n // 1. 初始化 idleSet(在加载首屏前,让预加载尽早启动)\n this.#initIdleSet();\n\n // 2. 首屏:匹配路由,已有 component 则跳过 load\n const result = match(path, this.routes);\n if (result) {\n const route = this.routes[result.path];\n\n if (route?.component) {\n // SSR 注入或构造函数传入的 component,直接复用\n route.status = 'loaded';\n } else {\n await this.#loadComponent(result.path);\n }\n\n this.active = {\n path,\n params: result.params,\n component: route?.component,\n };\n }\n\n this.stack = [{ path, params: this.active?.params ?? {} }];\n this.stackIndex = 0;\n this.#initBrowser();\n\n // 就绪:执行所有排队回调\n this.#inited = true;\n const q = this.#readyQueue;\n this.#readyQueue = [];\n for (const cb of q) cb();\n }\n\n // ====== 浏览器初始化 ======\n\n #initBrowser(): void {\n if (typeof window === 'undefined') return;\n\n // 劫持容器内链接点击\n document.addEventListener('click', this.#onClick);\n\n // 浏览器前进/后退\n window.addEventListener('popstate', this.#onPopstate);\n }\n\n #initIdleSet(): void {\n for (const path of Object.keys(this.routes)) {\n if (this.routes[path].status === 'idle') {\n this.idleSet.add(path);\n }\n }\n // 移除首屏路径\n if (this.active) {\n this.idleSet.delete(this.active.path);\n }\n }\n\n // ====== 五个公开方法 ======\n\n /** 导航到新页面(追加历史记录) */\n async pushState(url: string): Promise<void> {\n const result = match(url, this.routes);\n if (!result) return;\n\n const target: RouteEntry = { path: url, params: result.params };\n // 截断当前位置之后的历史,再追加\n this.stack.length = this.stackIndex + 1;\n this.stack.push(target);\n this.stackIndex = this.stack.length - 1;\n await this.#navigate(target);\n }\n\n /** 替换当前页面(不追加历史记录) */\n async replaceState(url: string): Promise<void> {\n const result = match(url, this.routes);\n if (!result) return;\n\n const target: RouteEntry = { path: url, params: result.params };\n this.stack[this.stackIndex] = target;\n await this.#navigate(target, { replace: true });\n }\n\n /** 后退 */\n async back(): Promise<void> {\n if (this.stackIndex <= 0) return;\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n\n const target = this.stack[this.stackIndex - 1];\n if (!(await this.#checkGuard(target, 'enter'))) return;\n\n history.back(); // popstate 触发 Index 同步\n }\n\n /** 前进 */\n async forward(): Promise<void> {\n if (this.stackIndex >= this.stack.length - 1) return;\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n\n const target = this.stack[this.stackIndex + 1];\n if (!(await this.#checkGuard(target, 'enter'))) return;\n\n history.forward(); // popstate 触发 Index 同步\n }\n\n /** 跳转多步 */\n async go(delta: number): Promise<void> {\n if (delta === 0) return;\n const newIdx = this.stackIndex + delta;\n if (newIdx < 0 || newIdx >= this.stack.length) return;\n\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n if (!(await this.#checkGuard(this.stack[newIdx], 'enter'))) return;\n\n history.go(delta);\n }\n\n // ====== 内部实现 ======\n\n private navId = 0;\n\n async #navigate(target: RouteEntry, opts: { replace?: boolean } = {}): Promise<void> {\n const id = ++this.navId;\n\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n if (id !== this.navId) return; // 守卫期间有新导航,丢弃本次\n if (!(await this.#checkGuard(target, 'enter'))) return;\n if (id !== this.navId) return;\n\n // 保存当前页滚动位置\n if (this.active && this.stack[this.stackIndex]) {\n this.stack[this.stackIndex].scroll = window.scrollY;\n }\n\n if (!opts.replace) {\n history.pushState(null, '', target.path);\n } else {\n history.replaceState(null, '', target.path);\n }\n\n // 加载组件\n await this.#loadComponent(target.path);\n if (id !== this.navId) return; // 加载期间有新导航,丢弃本次\n\n target.component = this.routes[target.path]?.component;\n this.active = target;\n\n // hash 滚动\n const hash = new URL(target.path, location.origin).hash;\n if (hash) {\n const el = document.querySelector(decodeURIComponent(hash));\n if (el) (el as HTMLElement).scrollIntoView({ behavior: 'smooth' });\n }\n\n this.#preloadNext();\n }\n\n async #checkGuard(\n entry: RouteEntry,\n type: 'enter' | 'leave'\n ): Promise<boolean> {\n if (type === 'enter' && this.enterGuard) {\n const result = await this.enterGuard(entry);\n if (result === false) return false;\n if (typeof result === 'object' && !result.ok) return false;\n }\n if (type === 'leave' && this.leaveGuard) {\n const result = await this.leaveGuard(entry);\n if (result === false) return false;\n if (typeof result === 'object' && !result.ok) return false;\n }\n return true;\n }\n\n // ====== 组件异步加载 ======\n\n async #loadComponent(path: string): Promise<any> {\n const route = this.routes[path];\n if (!route) return undefined;\n\n switch (route.status) {\n case 'loaded':\n return route.component;\n case 'loading':\n return route.promise;\n case 'error':\n break; // 可重试\n case 'idle':\n break;\n }\n\n if (!route.import) {\n throw new Error(`Route \"${path}\" has no import function`);\n }\n\n this.idleSet.delete(path);\n route.status = 'loading';\n route.promise = route\n .import()\n .then((mod: any) => {\n const Comp = mod.default || mod;\n route.status = 'loaded';\n route.component = Comp;\n return route.component;\n })\n .catch((err) => {\n route.status = 'error';\n throw err;\n })\n .finally(() => {\n this.loadingCount--;\n this.#preloadNext();\n });\n\n return route.promise;\n }\n\n // ====== 空闲预加载 ======\n\n #preloadNext(): void {\n while (this.loadingCount < this.maxPreload && this.idleSet.size > 0) {\n const path = this.idleSet.values().next().value!;\n this.loadingCount++;\n const scheduler =\n typeof requestIdleCallback !== 'undefined'\n ? requestIdleCallback\n : (fn: () => void) => setTimeout(fn, 0);\n scheduler(() => {\n this.#loadComponent(path);\n });\n }\n }\n\n // ====== 链接劫持 ======\n\n #onClick = (e: MouseEvent): void => {\n const link = (e.target as Element).closest('a');\n if (!link) return;\n\n const href = link.getAttribute('href');\n if (!href) return;\n\n // 纯 hash 链接放行,浏览器原生处理滚动\n if (href.startsWith('#')) return;\n\n try {\n const url = new URL(href, location.origin);\n // 外部链接不拦截\n if (url.origin !== location.origin) return;\n // 新窗口、下载、快捷键不拦截\n if (link.target === '_blank') return;\n if (link.hasAttribute('download')) return;\n if (e.ctrlKey || e.metaKey || e.shiftKey) return;\n\n e.preventDefault();\n this.pushState(url.pathname + url.search + url.hash);\n } catch {\n // 无效 URL,不拦截\n }\n };\n\n // ====== popstate 回调 ======\n\n #onPopstate = (): void => {\n const current = location.pathname + location.search;\n const idx = this.stack.findLastIndex((r) => r.path === current);\n\n if (idx !== -1) {\n // 仅 hash 变化,浏览器已更新 URL 并处理滚动,跳过 #navigate(避免 replaceState 抹掉 hash)\n if (idx === this.stackIndex) return;\n // 在栈中 → Router 产生的历史,移动指针\n this.stackIndex = idx;\n const entry = this.stack[idx];\n this.#navigate(entry, { replace: true });\n // 恢复滚动位置\n if (entry.scroll != null) {\n window.scrollTo(0, entry.scroll);\n }\n } else {\n // 不在栈中 → 外部跳转,重置栈\n const result = match(current, this.routes);\n const entry: RouteEntry = {\n path: current,\n params: result?.params ?? {},\n };\n this.stack = [entry];\n this.stackIndex = 0;\n this.active = entry;\n }\n };\n}\n"],"names":["compilePattern","pattern","paramNames","regexStr","replace","_","name","push","regex","RegExp","params","compiledCache","cacheKey","compileRoutes","map","Object","keys","filter","k","_compilePattern","match","path","normalized","startsWith","compiled","m","i","length","GlobalKey","createRouteRecord","opts","import","component","status","Router","Store","StoreIgnoreKeys","active","routes","menus","stack","stackIndex","idleSet","Set","maxPreload","loadingCount","ready","cb","Promise","resolve","constructor","opt","initialPath","globalThis","Routes","injectedMenus","Menus","Path","location","pathname","#init","result","route","q","#initBrowser","window","document","addEventListener","#initIdleSet","add","delete","pushState","url","target","replaceState","back","history","forward","go","delta","newIdx","navId","#navigate","id","scroll","scrollY","hash","URL","origin","el","querySelector","decodeURIComponent","scrollIntoView","behavior","#checkGuard","entry","type","enterGuard","ok","leaveGuard","#loadComponent","undefined","promise","Error","then","mod","Comp","default","catch","err","finally","#preloadNext","size","values","next","value","scheduler","requestIdleCallback","fn","setTimeout","e","link","closest","href","getAttribute","hasAttribute","ctrlKey","metaKey","shiftKey","preventDefault","search","#onPopstate","current","idx","findLastIndex","r","scrollTo"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Store, StoreIgnoreKeys } from 'aoye';
|
|
2
|
+
|
|
3
|
+
/** Router 构造选项 */
|
|
4
|
+
interface RouterOptions {
|
|
5
|
+
routes?: RouteMap;
|
|
6
|
+
initialPath?: string;
|
|
7
|
+
}
|
|
8
|
+
/** 路由表中每条记录 */
|
|
9
|
+
type RouteRecord = {
|
|
10
|
+
/** 异步 import 组件的函数(客户端 SPA 用) */
|
|
11
|
+
import?: () => Promise<{
|
|
12
|
+
default: typeof Store;
|
|
13
|
+
} | typeof Store>;
|
|
14
|
+
/** 同步组件类(SSR/SSG 用) */
|
|
15
|
+
component?: typeof Store;
|
|
16
|
+
/** 加载状态 */
|
|
17
|
+
status: 'idle' | 'loading' | 'loaded' | 'error';
|
|
18
|
+
/** 加载中的 Promise,避免重复 import */
|
|
19
|
+
promise?: Promise<any>;
|
|
20
|
+
/** 路径参数(动态路由时预填) */
|
|
21
|
+
params?: Record<string, string>;
|
|
22
|
+
};
|
|
23
|
+
/** 路由表 */
|
|
24
|
+
type RouteMap = Record<string, RouteRecord>;
|
|
25
|
+
/** 历史栈中的一条记录 */
|
|
26
|
+
type RouteEntry = {
|
|
27
|
+
path: string;
|
|
28
|
+
params: Record<string, string>;
|
|
29
|
+
/** 离开时的 scrollY,popstate 时恢复 */
|
|
30
|
+
scroll?: number;
|
|
31
|
+
/** 组件(类或实例,传给 bobe {active.component}) */
|
|
32
|
+
component?: typeof Store | Store;
|
|
33
|
+
};
|
|
34
|
+
/** 目录嵌套菜单 */
|
|
35
|
+
type Menu = {
|
|
36
|
+
/** 菜单名 */
|
|
37
|
+
name: string;
|
|
38
|
+
/** 文件路径,目录有 index 时等于 dir 路径,否则为空 */
|
|
39
|
+
path?: string;
|
|
40
|
+
/** 是否有对应组件(目录无 index 时为 false) */
|
|
41
|
+
hasComponent: boolean;
|
|
42
|
+
/** 子菜单 */
|
|
43
|
+
children?: Menu[];
|
|
44
|
+
};
|
|
45
|
+
/** 路由匹配结果 */
|
|
46
|
+
type MatchResult = {
|
|
47
|
+
path: string;
|
|
48
|
+
params: Record<string, string>;
|
|
49
|
+
};
|
|
50
|
+
/** 路由守卫返回 */
|
|
51
|
+
type GuardResult = boolean | {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
redirect?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 根据路径匹配路由表
|
|
58
|
+
* @param path - URL 路径,如 /post/42
|
|
59
|
+
* @param map - 路由表
|
|
60
|
+
* @returns 匹配结果,含路径参数;null 表示 404
|
|
61
|
+
*/
|
|
62
|
+
declare function match(path: string, map: RouteMap): MatchResult | null;
|
|
63
|
+
|
|
64
|
+
/** 创建带初始数据的 RouteRecord */
|
|
65
|
+
declare function createRouteRecord(opts?: Partial<Pick<RouteRecord, 'import' | 'component' | 'params'>>): RouteRecord;
|
|
66
|
+
declare class Router extends Store {
|
|
67
|
+
#private;
|
|
68
|
+
static [StoreIgnoreKeys]: string[];
|
|
69
|
+
/** 当前激活的路由,模板用 {active.component} 渲染 */
|
|
70
|
+
active: RouteEntry | null;
|
|
71
|
+
/** 路由表 */
|
|
72
|
+
routes: RouteMap;
|
|
73
|
+
/** 目录嵌套菜单 */
|
|
74
|
+
menus: Menu[];
|
|
75
|
+
/** 进入守卫 */
|
|
76
|
+
enterGuard?: (to: RouteEntry) => GuardResult | Promise<GuardResult>;
|
|
77
|
+
/** 离开守卫 */
|
|
78
|
+
leaveGuard?: (from: RouteEntry) => GuardResult | Promise<GuardResult>;
|
|
79
|
+
/** 历史栈(不响应式) */
|
|
80
|
+
private stack;
|
|
81
|
+
private stackIndex;
|
|
82
|
+
/** 待预加载的路径集合 */
|
|
83
|
+
private idleSet;
|
|
84
|
+
/** 最大并行预加载数 */
|
|
85
|
+
maxPreload: number;
|
|
86
|
+
/** 当前正在加载的组件数 */
|
|
87
|
+
private loadingCount;
|
|
88
|
+
/**
|
|
89
|
+
* 注册首屏就绪回调。
|
|
90
|
+
* - 已初始化 → 同步执行 cb
|
|
91
|
+
* - 未初始化 → 入队,首屏加载完成后执行
|
|
92
|
+
* 支持多次调用。
|
|
93
|
+
* 无参数时返回 Promise。
|
|
94
|
+
*/
|
|
95
|
+
ready(): Promise<void>;
|
|
96
|
+
ready(cb: () => void): void;
|
|
97
|
+
constructor(opt?: RouterOptions);
|
|
98
|
+
/** 导航到新页面(追加历史记录) */
|
|
99
|
+
pushState(url: string): Promise<void>;
|
|
100
|
+
/** 替换当前页面(不追加历史记录) */
|
|
101
|
+
replaceState(url: string): Promise<void>;
|
|
102
|
+
/** 后退 */
|
|
103
|
+
back(): Promise<void>;
|
|
104
|
+
/** 前进 */
|
|
105
|
+
forward(): Promise<void>;
|
|
106
|
+
/** 跳转多步 */
|
|
107
|
+
go(delta: number): Promise<void>;
|
|
108
|
+
private navId;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { Router, createRouteRecord, match };
|
|
112
|
+
export type { GuardResult, MatchResult, Menu, RouteEntry, RouteMap, RouteRecord };
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('aoye')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'aoye'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BobeRouter = {}, global.Aoye));
|
|
5
|
+
})(this, (function (exports, aoye) { 'use strict';
|
|
6
|
+
|
|
7
|
+
function compilePattern(pattern) {
|
|
8
|
+
const paramNames = [];
|
|
9
|
+
const regexStr = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/:([a-zA-Z_]\w*)/g, (_, name) => {
|
|
10
|
+
paramNames.push(name);
|
|
11
|
+
return '([^/]+)';
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
regex: new RegExp(`^${regexStr}$`),
|
|
15
|
+
params: paramNames
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
let compiledCache = null;
|
|
19
|
+
let cacheKey = null;
|
|
20
|
+
function compileRoutes(map) {
|
|
21
|
+
if (cacheKey === map && compiledCache) return compiledCache;
|
|
22
|
+
compiledCache = Object.keys(map).filter(k => k !== '*').map(pattern => {
|
|
23
|
+
const _compilePattern = compilePattern(pattern),
|
|
24
|
+
regex = _compilePattern.regex,
|
|
25
|
+
paramNames = _compilePattern.params;
|
|
26
|
+
return {
|
|
27
|
+
pattern,
|
|
28
|
+
regex,
|
|
29
|
+
paramNames
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
if (map['*']) {
|
|
33
|
+
compiledCache.push({
|
|
34
|
+
pattern: '*',
|
|
35
|
+
regex: /.*/,
|
|
36
|
+
paramNames: []
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
cacheKey = map;
|
|
40
|
+
return compiledCache;
|
|
41
|
+
}
|
|
42
|
+
function match(path, map) {
|
|
43
|
+
const normalized = path.startsWith('/') ? path : `/${path}`;
|
|
44
|
+
for (const compiled of compileRoutes(map)) {
|
|
45
|
+
const m = normalized.match(compiled.regex);
|
|
46
|
+
if (!m) continue;
|
|
47
|
+
const params = {};
|
|
48
|
+
for (let i = 0; i < compiled.paramNames.length; i++) {
|
|
49
|
+
params[compiled.paramNames[i]] = m[i + 1];
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
path: compiled.pattern,
|
|
53
|
+
params
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let GlobalKey = function (GlobalKey) {
|
|
60
|
+
GlobalKey["Routes"] = "__BOBE_INIT_ROUTES__";
|
|
61
|
+
GlobalKey["Menus"] = "__BOBE_INIT_MENUS__";
|
|
62
|
+
GlobalKey["Path"] = "__BOBE_INIT_PATH__";
|
|
63
|
+
return GlobalKey;
|
|
64
|
+
}({});
|
|
65
|
+
|
|
66
|
+
function createRouteRecord(opts = {}) {
|
|
67
|
+
return {
|
|
68
|
+
import: opts.import,
|
|
69
|
+
component: opts.component,
|
|
70
|
+
status: opts.component ? 'loaded' : 'idle',
|
|
71
|
+
params: opts.params
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
class Router extends aoye.Store {
|
|
75
|
+
static [aoye.StoreIgnoreKeys] = ['routes', 'menus', 'stack', 'ready'];
|
|
76
|
+
active = null;
|
|
77
|
+
routes = {};
|
|
78
|
+
menus = [];
|
|
79
|
+
stack = [];
|
|
80
|
+
stackIndex = 0;
|
|
81
|
+
idleSet = new Set();
|
|
82
|
+
maxPreload = 3;
|
|
83
|
+
loadingCount = 0;
|
|
84
|
+
ready(cb) {
|
|
85
|
+
if (cb) {
|
|
86
|
+
if (this.#inited) {
|
|
87
|
+
cb();
|
|
88
|
+
} else {
|
|
89
|
+
this.#readyQueue.push(cb);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
return new Promise(resolve => this.ready(resolve));
|
|
94
|
+
}
|
|
95
|
+
#inited = false;
|
|
96
|
+
#readyQueue = [];
|
|
97
|
+
constructor(opt) {
|
|
98
|
+
super();
|
|
99
|
+
const routes = opt?.routes;
|
|
100
|
+
const initialPath = opt?.initialPath;
|
|
101
|
+
this.routes = routes || globalThis[GlobalKey.Routes] || {};
|
|
102
|
+
const injectedMenus = globalThis[GlobalKey.Menus];
|
|
103
|
+
if (injectedMenus) this.menus = injectedMenus;
|
|
104
|
+
const path = initialPath || globalThis[GlobalKey.Path] || (typeof location !== 'undefined' ? location.pathname : '/');
|
|
105
|
+
this.#init(path);
|
|
106
|
+
}
|
|
107
|
+
async #init(path) {
|
|
108
|
+
this.#initIdleSet();
|
|
109
|
+
const result = match(path, this.routes);
|
|
110
|
+
if (result) {
|
|
111
|
+
const route = this.routes[result.path];
|
|
112
|
+
if (route?.component) {
|
|
113
|
+
route.status = 'loaded';
|
|
114
|
+
} else {
|
|
115
|
+
await this.#loadComponent(result.path);
|
|
116
|
+
}
|
|
117
|
+
this.active = {
|
|
118
|
+
path,
|
|
119
|
+
params: result.params,
|
|
120
|
+
component: route?.component
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
this.stack = [{
|
|
124
|
+
path,
|
|
125
|
+
params: this.active?.params ?? {}
|
|
126
|
+
}];
|
|
127
|
+
this.stackIndex = 0;
|
|
128
|
+
this.#initBrowser();
|
|
129
|
+
this.#inited = true;
|
|
130
|
+
const q = this.#readyQueue;
|
|
131
|
+
this.#readyQueue = [];
|
|
132
|
+
for (const cb of q) cb();
|
|
133
|
+
}
|
|
134
|
+
#initBrowser() {
|
|
135
|
+
if (typeof window === 'undefined') return;
|
|
136
|
+
document.addEventListener('click', this.#onClick);
|
|
137
|
+
window.addEventListener('popstate', this.#onPopstate);
|
|
138
|
+
}
|
|
139
|
+
#initIdleSet() {
|
|
140
|
+
for (const path of Object.keys(this.routes)) {
|
|
141
|
+
if (this.routes[path].status === 'idle') {
|
|
142
|
+
this.idleSet.add(path);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (this.active) {
|
|
146
|
+
this.idleSet.delete(this.active.path);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async pushState(url) {
|
|
150
|
+
const result = match(url, this.routes);
|
|
151
|
+
if (!result) return;
|
|
152
|
+
const target = {
|
|
153
|
+
path: url,
|
|
154
|
+
params: result.params
|
|
155
|
+
};
|
|
156
|
+
this.stack.length = this.stackIndex + 1;
|
|
157
|
+
this.stack.push(target);
|
|
158
|
+
this.stackIndex = this.stack.length - 1;
|
|
159
|
+
await this.#navigate(target);
|
|
160
|
+
}
|
|
161
|
+
async replaceState(url) {
|
|
162
|
+
const result = match(url, this.routes);
|
|
163
|
+
if (!result) return;
|
|
164
|
+
const target = {
|
|
165
|
+
path: url,
|
|
166
|
+
params: result.params
|
|
167
|
+
};
|
|
168
|
+
this.stack[this.stackIndex] = target;
|
|
169
|
+
await this.#navigate(target, {
|
|
170
|
+
replace: true
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
async back() {
|
|
174
|
+
if (this.stackIndex <= 0) return;
|
|
175
|
+
if (!(await this.#checkGuard(this.active, 'leave'))) return;
|
|
176
|
+
const target = this.stack[this.stackIndex - 1];
|
|
177
|
+
if (!(await this.#checkGuard(target, 'enter'))) return;
|
|
178
|
+
history.back();
|
|
179
|
+
}
|
|
180
|
+
async forward() {
|
|
181
|
+
if (this.stackIndex >= this.stack.length - 1) return;
|
|
182
|
+
if (!(await this.#checkGuard(this.active, 'leave'))) return;
|
|
183
|
+
const target = this.stack[this.stackIndex + 1];
|
|
184
|
+
if (!(await this.#checkGuard(target, 'enter'))) return;
|
|
185
|
+
history.forward();
|
|
186
|
+
}
|
|
187
|
+
async go(delta) {
|
|
188
|
+
if (delta === 0) return;
|
|
189
|
+
const newIdx = this.stackIndex + delta;
|
|
190
|
+
if (newIdx < 0 || newIdx >= this.stack.length) return;
|
|
191
|
+
if (!(await this.#checkGuard(this.active, 'leave'))) return;
|
|
192
|
+
if (!(await this.#checkGuard(this.stack[newIdx], 'enter'))) return;
|
|
193
|
+
history.go(delta);
|
|
194
|
+
}
|
|
195
|
+
navId = 0;
|
|
196
|
+
async #navigate(target, opts = {}) {
|
|
197
|
+
const id = ++this.navId;
|
|
198
|
+
if (!(await this.#checkGuard(this.active, 'leave'))) return;
|
|
199
|
+
if (id !== this.navId) return;
|
|
200
|
+
if (!(await this.#checkGuard(target, 'enter'))) return;
|
|
201
|
+
if (id !== this.navId) return;
|
|
202
|
+
if (this.active && this.stack[this.stackIndex]) {
|
|
203
|
+
this.stack[this.stackIndex].scroll = window.scrollY;
|
|
204
|
+
}
|
|
205
|
+
if (!opts.replace) {
|
|
206
|
+
history.pushState(null, '', target.path);
|
|
207
|
+
} else {
|
|
208
|
+
history.replaceState(null, '', target.path);
|
|
209
|
+
}
|
|
210
|
+
await this.#loadComponent(target.path);
|
|
211
|
+
if (id !== this.navId) return;
|
|
212
|
+
target.component = this.routes[target.path]?.component;
|
|
213
|
+
this.active = target;
|
|
214
|
+
const hash = new URL(target.path, location.origin).hash;
|
|
215
|
+
if (hash) {
|
|
216
|
+
const el = document.querySelector(decodeURIComponent(hash));
|
|
217
|
+
if (el) el.scrollIntoView({
|
|
218
|
+
behavior: 'smooth'
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
this.#preloadNext();
|
|
222
|
+
}
|
|
223
|
+
async #checkGuard(entry, type) {
|
|
224
|
+
if (type === 'enter' && this.enterGuard) {
|
|
225
|
+
const result = await this.enterGuard(entry);
|
|
226
|
+
if (result === false) return false;
|
|
227
|
+
if (typeof result === 'object' && !result.ok) return false;
|
|
228
|
+
}
|
|
229
|
+
if (type === 'leave' && this.leaveGuard) {
|
|
230
|
+
const result = await this.leaveGuard(entry);
|
|
231
|
+
if (result === false) return false;
|
|
232
|
+
if (typeof result === 'object' && !result.ok) return false;
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
async #loadComponent(path) {
|
|
237
|
+
const route = this.routes[path];
|
|
238
|
+
if (!route) return undefined;
|
|
239
|
+
switch (route.status) {
|
|
240
|
+
case 'loaded':
|
|
241
|
+
return route.component;
|
|
242
|
+
case 'loading':
|
|
243
|
+
return route.promise;
|
|
244
|
+
}
|
|
245
|
+
if (!route.import) {
|
|
246
|
+
throw new Error(`Route "${path}" has no import function`);
|
|
247
|
+
}
|
|
248
|
+
this.idleSet.delete(path);
|
|
249
|
+
route.status = 'loading';
|
|
250
|
+
route.promise = route.import().then(mod => {
|
|
251
|
+
const Comp = mod.default || mod;
|
|
252
|
+
route.status = 'loaded';
|
|
253
|
+
route.component = Comp;
|
|
254
|
+
return route.component;
|
|
255
|
+
}).catch(err => {
|
|
256
|
+
route.status = 'error';
|
|
257
|
+
throw err;
|
|
258
|
+
}).finally(() => {
|
|
259
|
+
this.loadingCount--;
|
|
260
|
+
this.#preloadNext();
|
|
261
|
+
});
|
|
262
|
+
return route.promise;
|
|
263
|
+
}
|
|
264
|
+
#preloadNext() {
|
|
265
|
+
while (this.loadingCount < this.maxPreload && this.idleSet.size > 0) {
|
|
266
|
+
const path = this.idleSet.values().next().value;
|
|
267
|
+
this.loadingCount++;
|
|
268
|
+
const scheduler = typeof requestIdleCallback !== 'undefined' ? requestIdleCallback : fn => setTimeout(fn, 0);
|
|
269
|
+
scheduler(() => {
|
|
270
|
+
this.#loadComponent(path);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
#onClick = e => {
|
|
275
|
+
const link = e.target.closest('a');
|
|
276
|
+
if (!link) return;
|
|
277
|
+
const href = link.getAttribute('href');
|
|
278
|
+
if (!href) return;
|
|
279
|
+
if (href.startsWith('#')) return;
|
|
280
|
+
try {
|
|
281
|
+
const url = new URL(href, location.origin);
|
|
282
|
+
if (url.origin !== location.origin) return;
|
|
283
|
+
if (link.target === '_blank') return;
|
|
284
|
+
if (link.hasAttribute('download')) return;
|
|
285
|
+
if (e.ctrlKey || e.metaKey || e.shiftKey) return;
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
this.pushState(url.pathname + url.search + url.hash);
|
|
288
|
+
} catch {}
|
|
289
|
+
};
|
|
290
|
+
#onPopstate = () => {
|
|
291
|
+
const current = location.pathname + location.search;
|
|
292
|
+
const idx = this.stack.findLastIndex(r => r.path === current);
|
|
293
|
+
if (idx !== -1) {
|
|
294
|
+
if (idx === this.stackIndex) return;
|
|
295
|
+
this.stackIndex = idx;
|
|
296
|
+
const entry = this.stack[idx];
|
|
297
|
+
this.#navigate(entry, {
|
|
298
|
+
replace: true
|
|
299
|
+
});
|
|
300
|
+
if (entry.scroll != null) {
|
|
301
|
+
window.scrollTo(0, entry.scroll);
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
const result = match(current, this.routes);
|
|
305
|
+
const entry = {
|
|
306
|
+
path: current,
|
|
307
|
+
params: result?.params ?? {}
|
|
308
|
+
};
|
|
309
|
+
this.stack = [entry];
|
|
310
|
+
this.stackIndex = 0;
|
|
311
|
+
this.active = entry;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
exports.Router = Router;
|
|
317
|
+
exports.createRouteRecord = createRouteRecord;
|
|
318
|
+
exports.match = match;
|
|
319
|
+
|
|
320
|
+
}));
|
|
321
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/match.ts","../src/global.ts","../src/router.ts"],"sourcesContent":["import type { RouteMap, MatchResult } from './type';\n\n/** 编译路径模式为正则:/post/:id → ^\\/post\\/([^/]+)$,捕获名为 id */\nfunction compilePattern(pattern: string): { regex: RegExp; params: string[] } {\n const paramNames: string[] = [];\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // 转义正则特殊字符\n .replace(/:([a-zA-Z_]\\w*)/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n return {\n regex: new RegExp(`^${regexStr}$`),\n params: paramNames,\n };\n}\n\n/** 预编译所有路由的正则 */\ntype CompiledRoute = {\n pattern: string;\n regex: RegExp;\n paramNames: string[];\n};\n\nlet compiledCache: CompiledRoute[] | null = null;\nlet cacheKey: RouteMap | null = null;\n\nfunction compileRoutes(map: RouteMap): CompiledRoute[] {\n if (cacheKey === map && compiledCache) return compiledCache;\n compiledCache = Object.keys(map)\n .filter((k) => k !== '*') // 通配符最后匹配\n .map((pattern) => {\n const { regex, params: paramNames } = compilePattern(pattern);\n return { pattern, regex, paramNames };\n });\n // 通配符路由放最后\n if (map['*']) {\n compiledCache.push({ pattern: '*', regex: /.*/, paramNames: [] });\n }\n cacheKey = map;\n return compiledCache;\n}\n\n/**\n * 根据路径匹配路由表\n * @param path - URL 路径,如 /post/42\n * @param map - 路由表\n * @returns 匹配结果,含路径参数;null 表示 404\n */\nexport function match(path: string, map: RouteMap): MatchResult | null {\n // 确保以 / 开头\n const normalized = path.startsWith('/') ? path : `/${path}`;\n\n for (const compiled of compileRoutes(map)) {\n const m = normalized.match(compiled.regex);\n if (!m) continue;\n\n const params: Record<string, string> = {};\n for (let i = 0; i < compiled.paramNames.length; i++) {\n params[compiled.paramNames[i]] = m[i + 1];\n }\n return { path: compiled.pattern, params };\n }\n return null;\n}\n","/** 通过 globalThis 传递的路由全局变量名 */\nexport enum GlobalKey {\n /** 路由表:{ [url]: { import/component } } */\n Routes = '__BOBE_INIT_ROUTES__',\n /** 菜单树:Menu[] */\n Menus = '__BOBE_INIT_MENUS__',\n /** 初始路径 */\n Path = '__BOBE_INIT_PATH__',\n}\n","import { Store, StoreIgnoreKeys } from 'aoye';\nimport type { RouteMap, RouteEntry, RouteRecord, GuardResult, Menu, RouterOptions } from './type';\nimport { match } from './match';\nimport { GlobalKey } from './global';\n\n/** 创建带初始数据的 RouteRecord */\nexport function createRouteRecord(\n opts: Partial<Pick<RouteRecord, 'import' | 'component' | 'params'>> = {}\n): RouteRecord {\n return {\n import: opts.import,\n component: opts.component,\n status: opts.component ? 'loaded' : 'idle',\n params: opts.params,\n };\n}\n\nexport class Router extends Store {\n static [StoreIgnoreKeys] = ['routes', 'menus', 'stack', 'ready'] as string[];\n\n /** 当前激活的路由,模板用 {active.component} 渲染 */\n active: RouteEntry | null = null;\n\n /** 路由表 */\n routes: RouteMap = {};\n\n /** 目录嵌套菜单 */\n menus: Menu[] = [];\n\n /** 进入守卫 */\n enterGuard?: (to: RouteEntry) => GuardResult | Promise<GuardResult>;\n\n /** 离开守卫 */\n leaveGuard?: (from: RouteEntry) => GuardResult | Promise<GuardResult>;\n\n /** 历史栈(不响应式) */\n private stack: RouteEntry[] = [];\n private stackIndex = 0;\n\n /** 待预加载的路径集合 */\n private idleSet = new Set<string>();\n\n /** 最大并行预加载数 */\n maxPreload = 3;\n\n /** 当前正在加载的组件数 */\n private loadingCount = 0;\n\n /**\n * 注册首屏就绪回调。\n * - 已初始化 → 同步执行 cb\n * - 未初始化 → 入队,首屏加载完成后执行\n * 支持多次调用。\n * 无参数时返回 Promise。\n */\n ready(): Promise<void>;\n ready(cb: () => void): void;\n ready(cb?: () => void): Promise<void> | void {\n if (cb) {\n if (this.#inited) { cb(); }\n else { this.#readyQueue.push(cb); }\n return;\n }\n return new Promise<void>(resolve => this.ready(resolve));\n }\n\n #inited = false;\n #readyQueue: (() => void)[] = [];\n\n constructor(opt?: RouterOptions) {\n super();\n\n const routes = opt?.routes;\n const initialPath = opt?.initialPath;\n\n // 1. routes 优先级:用户传入 > SSR 注入 > 空\n this.routes = routes\n || (globalThis as any)[GlobalKey.Routes]\n || {};\n\n // 2. menus 优先级:SSR 注入 > 空\n const injectedMenus = (globalThis as any)[GlobalKey.Menus];\n if (injectedMenus) this.menus = injectedMenus;\n\n // 3. path 优先级:用户传入 > SSR 注入 > location > '/'\n const path = initialPath\n || (globalThis as any)[GlobalKey.Path]\n || (typeof location !== 'undefined' ? location.pathname : '/');\n\n this.#init(path);\n }\n\n // ====== 初始化 ======\n async #init(path: string): Promise<void> {\n\n // 1. 初始化 idleSet(在加载首屏前,让预加载尽早启动)\n this.#initIdleSet();\n\n // 2. 首屏:匹配路由,已有 component 则跳过 load\n const result = match(path, this.routes);\n if (result) {\n const route = this.routes[result.path];\n\n if (route?.component) {\n // SSR 注入或构造函数传入的 component,直接复用\n route.status = 'loaded';\n } else {\n await this.#loadComponent(result.path);\n }\n\n this.active = {\n path,\n params: result.params,\n component: route?.component,\n };\n }\n\n this.stack = [{ path, params: this.active?.params ?? {} }];\n this.stackIndex = 0;\n this.#initBrowser();\n\n // 就绪:执行所有排队回调\n this.#inited = true;\n const q = this.#readyQueue;\n this.#readyQueue = [];\n for (const cb of q) cb();\n }\n\n // ====== 浏览器初始化 ======\n\n #initBrowser(): void {\n if (typeof window === 'undefined') return;\n\n // 劫持容器内链接点击\n document.addEventListener('click', this.#onClick);\n\n // 浏览器前进/后退\n window.addEventListener('popstate', this.#onPopstate);\n }\n\n #initIdleSet(): void {\n for (const path of Object.keys(this.routes)) {\n if (this.routes[path].status === 'idle') {\n this.idleSet.add(path);\n }\n }\n // 移除首屏路径\n if (this.active) {\n this.idleSet.delete(this.active.path);\n }\n }\n\n // ====== 五个公开方法 ======\n\n /** 导航到新页面(追加历史记录) */\n async pushState(url: string): Promise<void> {\n const result = match(url, this.routes);\n if (!result) return;\n\n const target: RouteEntry = { path: url, params: result.params };\n // 截断当前位置之后的历史,再追加\n this.stack.length = this.stackIndex + 1;\n this.stack.push(target);\n this.stackIndex = this.stack.length - 1;\n await this.#navigate(target);\n }\n\n /** 替换当前页面(不追加历史记录) */\n async replaceState(url: string): Promise<void> {\n const result = match(url, this.routes);\n if (!result) return;\n\n const target: RouteEntry = { path: url, params: result.params };\n this.stack[this.stackIndex] = target;\n await this.#navigate(target, { replace: true });\n }\n\n /** 后退 */\n async back(): Promise<void> {\n if (this.stackIndex <= 0) return;\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n\n const target = this.stack[this.stackIndex - 1];\n if (!(await this.#checkGuard(target, 'enter'))) return;\n\n history.back(); // popstate 触发 Index 同步\n }\n\n /** 前进 */\n async forward(): Promise<void> {\n if (this.stackIndex >= this.stack.length - 1) return;\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n\n const target = this.stack[this.stackIndex + 1];\n if (!(await this.#checkGuard(target, 'enter'))) return;\n\n history.forward(); // popstate 触发 Index 同步\n }\n\n /** 跳转多步 */\n async go(delta: number): Promise<void> {\n if (delta === 0) return;\n const newIdx = this.stackIndex + delta;\n if (newIdx < 0 || newIdx >= this.stack.length) return;\n\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n if (!(await this.#checkGuard(this.stack[newIdx], 'enter'))) return;\n\n history.go(delta);\n }\n\n // ====== 内部实现 ======\n\n private navId = 0;\n\n async #navigate(target: RouteEntry, opts: { replace?: boolean } = {}): Promise<void> {\n const id = ++this.navId;\n\n if (!(await this.#checkGuard(this.active!, 'leave'))) return;\n if (id !== this.navId) return; // 守卫期间有新导航,丢弃本次\n if (!(await this.#checkGuard(target, 'enter'))) return;\n if (id !== this.navId) return;\n\n // 保存当前页滚动位置\n if (this.active && this.stack[this.stackIndex]) {\n this.stack[this.stackIndex].scroll = window.scrollY;\n }\n\n if (!opts.replace) {\n history.pushState(null, '', target.path);\n } else {\n history.replaceState(null, '', target.path);\n }\n\n // 加载组件\n await this.#loadComponent(target.path);\n if (id !== this.navId) return; // 加载期间有新导航,丢弃本次\n\n target.component = this.routes[target.path]?.component;\n this.active = target;\n\n // hash 滚动\n const hash = new URL(target.path, location.origin).hash;\n if (hash) {\n const el = document.querySelector(decodeURIComponent(hash));\n if (el) (el as HTMLElement).scrollIntoView({ behavior: 'smooth' });\n }\n\n this.#preloadNext();\n }\n\n async #checkGuard(\n entry: RouteEntry,\n type: 'enter' | 'leave'\n ): Promise<boolean> {\n if (type === 'enter' && this.enterGuard) {\n const result = await this.enterGuard(entry);\n if (result === false) return false;\n if (typeof result === 'object' && !result.ok) return false;\n }\n if (type === 'leave' && this.leaveGuard) {\n const result = await this.leaveGuard(entry);\n if (result === false) return false;\n if (typeof result === 'object' && !result.ok) return false;\n }\n return true;\n }\n\n // ====== 组件异步加载 ======\n\n async #loadComponent(path: string): Promise<any> {\n const route = this.routes[path];\n if (!route) return undefined;\n\n switch (route.status) {\n case 'loaded':\n return route.component;\n case 'loading':\n return route.promise;\n case 'error':\n break; // 可重试\n case 'idle':\n break;\n }\n\n if (!route.import) {\n throw new Error(`Route \"${path}\" has no import function`);\n }\n\n this.idleSet.delete(path);\n route.status = 'loading';\n route.promise = route\n .import()\n .then((mod: any) => {\n const Comp = mod.default || mod;\n route.status = 'loaded';\n route.component = Comp;\n return route.component;\n })\n .catch((err) => {\n route.status = 'error';\n throw err;\n })\n .finally(() => {\n this.loadingCount--;\n this.#preloadNext();\n });\n\n return route.promise;\n }\n\n // ====== 空闲预加载 ======\n\n #preloadNext(): void {\n while (this.loadingCount < this.maxPreload && this.idleSet.size > 0) {\n const path = this.idleSet.values().next().value!;\n this.loadingCount++;\n const scheduler =\n typeof requestIdleCallback !== 'undefined'\n ? requestIdleCallback\n : (fn: () => void) => setTimeout(fn, 0);\n scheduler(() => {\n this.#loadComponent(path);\n });\n }\n }\n\n // ====== 链接劫持 ======\n\n #onClick = (e: MouseEvent): void => {\n const link = (e.target as Element).closest('a');\n if (!link) return;\n\n const href = link.getAttribute('href');\n if (!href) return;\n\n // 纯 hash 链接放行,浏览器原生处理滚动\n if (href.startsWith('#')) return;\n\n try {\n const url = new URL(href, location.origin);\n // 外部链接不拦截\n if (url.origin !== location.origin) return;\n // 新窗口、下载、快捷键不拦截\n if (link.target === '_blank') return;\n if (link.hasAttribute('download')) return;\n if (e.ctrlKey || e.metaKey || e.shiftKey) return;\n\n e.preventDefault();\n this.pushState(url.pathname + url.search + url.hash);\n } catch {\n // 无效 URL,不拦截\n }\n };\n\n // ====== popstate 回调 ======\n\n #onPopstate = (): void => {\n const current = location.pathname + location.search;\n const idx = this.stack.findLastIndex((r) => r.path === current);\n\n if (idx !== -1) {\n // 仅 hash 变化,浏览器已更新 URL 并处理滚动,跳过 #navigate(避免 replaceState 抹掉 hash)\n if (idx === this.stackIndex) return;\n // 在栈中 → Router 产生的历史,移动指针\n this.stackIndex = idx;\n const entry = this.stack[idx];\n this.#navigate(entry, { replace: true });\n // 恢复滚动位置\n if (entry.scroll != null) {\n window.scrollTo(0, entry.scroll);\n }\n } else {\n // 不在栈中 → 外部跳转,重置栈\n const result = match(current, this.routes);\n const entry: RouteEntry = {\n path: current,\n params: result?.params ?? {},\n };\n this.stack = [entry];\n this.stackIndex = 0;\n this.active = entry;\n }\n };\n}\n"],"names":["compilePattern","pattern","paramNames","regexStr","replace","_","name","push","regex","RegExp","params","compiledCache","cacheKey","compileRoutes","map","Object","keys","filter","k","_compilePattern","match","path","normalized","startsWith","compiled","m","i","length","GlobalKey","createRouteRecord","opts","import","component","status","Router","Store","StoreIgnoreKeys","active","routes","menus","stack","stackIndex","idleSet","Set","maxPreload","loadingCount","ready","cb","Promise","resolve","constructor","opt","initialPath","globalThis","Routes","injectedMenus","Menus","Path","location","pathname","#init","result","route","q","#initBrowser","window","document","addEventListener","#initIdleSet","add","delete","pushState","url","target","replaceState","back","history","forward","go","delta","newIdx","navId","#navigate","id","scroll","scrollY","hash","URL","origin","el","querySelector","decodeURIComponent","scrollIntoView","behavior","#checkGuard","entry","type","enterGuard","ok","leaveGuard","#loadComponent","undefined","promise","Error","then","mod","Comp","default","catch","err","finally","#preloadNext","size","values","next","value","scheduler","requestIdleCallback","fn","setTimeout","e","link","closest","href","getAttribute","hasAttribute","ctrlKey","metaKey","shiftKey","preventDefault","search","#onPopstate","current","idx","findLastIndex","r","scrollTo"],"mappings":"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"}
|