bobe-router 0.0.65 → 0.0.67

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.
@@ -66,7 +66,9 @@ function createRouteRecord(opts = {}) {
66
66
  import: opts.import,
67
67
  component: opts.component,
68
68
  status: opts.component ? 'loaded' : 'idle',
69
- params: opts.params
69
+ params: opts.params,
70
+ meta: opts.meta,
71
+ layout: opts.layout
70
72
  };
71
73
  }
72
74
  class Router extends aoye.Store {
@@ -115,7 +117,9 @@ class Router extends aoye.Store {
115
117
  this.active = {
116
118
  path,
117
119
  params: result.params,
118
- component: route?.component
120
+ component: route?.component,
121
+ meta: route?.meta,
122
+ layout: route?.layout
119
123
  };
120
124
  }
121
125
  this.stack = [{
@@ -207,7 +211,10 @@ class Router extends aoye.Store {
207
211
  }
208
212
  await this.#loadComponent(target.path);
209
213
  if (id !== this.navId) return;
210
- target.component = this.routes[target.path]?.component;
214
+ const route = this.routes[target.path];
215
+ target.component = route?.component;
216
+ target.meta = route?.meta;
217
+ target.layout = route?.layout;
211
218
  this.active = target;
212
219
  const hash = new URL(target.path, location.origin).hash;
213
220
  if (hash) {
@@ -249,6 +256,12 @@ class Router extends aoye.Store {
249
256
  const Comp = mod.default || mod;
250
257
  route.status = 'loaded';
251
258
  route.component = Comp;
259
+ if (!route.meta && mod.routeMeta) {
260
+ route.meta = mod.routeMeta;
261
+ }
262
+ if (!route.layout && mod.layout) {
263
+ route.layout = mod.layout;
264
+ }
252
265
  return route.component;
253
266
  }).catch(err => {
254
267
  route.status = 'error';
@@ -300,9 +313,12 @@ class Router extends aoye.Store {
300
313
  }
301
314
  } else {
302
315
  const result = match(current, this.routes);
316
+ const route = result ? this.routes[result.path] : undefined;
303
317
  const entry = {
304
318
  path: current,
305
- params: result?.params ?? {}
319
+ params: result?.params ?? {},
320
+ meta: route?.meta,
321
+ layout: route?.layout
306
322
  };
307
323
  this.stack = [entry];
308
324
  this.stackIndex = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"bobe-router.cjs.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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"}
1
+ {"version":3,"file":"bobe-router.cjs.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' | 'meta' | 'layout'>> = {}\n): RouteRecord {\n return {\n import: opts.import,\n component: opts.component,\n status: opts.component ? 'loaded' : 'idle',\n params: opts.params,\n meta: opts.meta,\n layout: opts.layout,\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 meta: route?.meta,\n layout: route?.layout,\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 const route = this.routes[target.path];\n target.component = route?.component;\n target.meta = route?.meta;\n target.layout = route?.layout;\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 // 从模块 named export 提取 routeMeta(构建时未提取到时的回退)\n if (!route.meta && mod.routeMeta) {\n route.meta = mod.routeMeta;\n }\n // 从模块 named export 提取 layout(若未显式设置)\n if (!route.layout && mod.layout) {\n route.layout = mod.layout;\n }\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 route = result ? this.routes[result.path] : undefined;\n const entry: RouteEntry = {\n path: current,\n params: result?.params ?? {},\n meta: route?.meta,\n layout: route?.layout,\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","meta","layout","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","routeMeta","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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"}
@@ -64,7 +64,9 @@ function createRouteRecord(opts = {}) {
64
64
  import: opts.import,
65
65
  component: opts.component,
66
66
  status: opts.component ? 'loaded' : 'idle',
67
- params: opts.params
67
+ params: opts.params,
68
+ meta: opts.meta,
69
+ layout: opts.layout
68
70
  };
69
71
  }
70
72
  class Router extends Store {
@@ -113,7 +115,9 @@ class Router extends Store {
113
115
  this.active = {
114
116
  path,
115
117
  params: result.params,
116
- component: route?.component
118
+ component: route?.component,
119
+ meta: route?.meta,
120
+ layout: route?.layout
117
121
  };
118
122
  }
119
123
  this.stack = [{
@@ -205,7 +209,10 @@ class Router extends Store {
205
209
  }
206
210
  await this.#loadComponent(target.path);
207
211
  if (id !== this.navId) return;
208
- target.component = this.routes[target.path]?.component;
212
+ const route = this.routes[target.path];
213
+ target.component = route?.component;
214
+ target.meta = route?.meta;
215
+ target.layout = route?.layout;
209
216
  this.active = target;
210
217
  const hash = new URL(target.path, location.origin).hash;
211
218
  if (hash) {
@@ -247,6 +254,12 @@ class Router extends Store {
247
254
  const Comp = mod.default || mod;
248
255
  route.status = 'loaded';
249
256
  route.component = Comp;
257
+ if (!route.meta && mod.routeMeta) {
258
+ route.meta = mod.routeMeta;
259
+ }
260
+ if (!route.layout && mod.layout) {
261
+ route.layout = mod.layout;
262
+ }
250
263
  return route.component;
251
264
  }).catch(err => {
252
265
  route.status = 'error';
@@ -298,9 +311,12 @@ class Router extends Store {
298
311
  }
299
312
  } else {
300
313
  const result = match(current, this.routes);
314
+ const route = result ? this.routes[result.path] : undefined;
301
315
  const entry = {
302
316
  path: current,
303
- params: result?.params ?? {}
317
+ params: result?.params ?? {},
318
+ meta: route?.meta,
319
+ layout: route?.layout
304
320
  };
305
321
  this.stack = [entry];
306
322
  this.stackIndex = 0;
@@ -1 +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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"}
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' | 'meta' | 'layout'>> = {}\n): RouteRecord {\n return {\n import: opts.import,\n component: opts.component,\n status: opts.component ? 'loaded' : 'idle',\n params: opts.params,\n meta: opts.meta,\n layout: opts.layout,\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 meta: route?.meta,\n layout: route?.layout,\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 const route = this.routes[target.path];\n target.component = route?.component;\n target.meta = route?.meta;\n target.layout = route?.layout;\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 // 从模块 named export 提取 routeMeta(构建时未提取到时的回退)\n if (!route.meta && mod.routeMeta) {\n route.meta = mod.routeMeta;\n }\n // 从模块 named export 提取 layout(若未显式设置)\n if (!route.layout && mod.layout) {\n route.layout = mod.layout;\n }\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 route = result ? this.routes[result.path] : undefined;\n const entry: RouteEntry = {\n path: current,\n params: result?.params ?? {},\n meta: route?.meta,\n layout: route?.layout,\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","meta","layout","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","routeMeta","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 CHANGED
@@ -19,6 +19,12 @@ type RouteRecord = {
19
19
  promise?: Promise<any>;
20
20
  /** 路径参数(动态路由时预填) */
21
21
  params?: Record<string, string>;
22
+ /** 自定义路由元信息(构建时从 export const routeMeta 提取,或手动传入) */
23
+ meta?: Record<string, any>;
24
+ /** 布局组件(从模块 named export 获取,或手动传入) */
25
+ layout?: typeof Store | (() => Promise<{
26
+ default: typeof Store;
27
+ } | typeof Store>);
22
28
  };
23
29
  /** 路由表 */
24
30
  type RouteMap = Record<string, RouteRecord>;
@@ -30,6 +36,12 @@ type RouteEntry = {
30
36
  scroll?: number;
31
37
  /** 组件(类或实例,传给 bobe {active.component}) */
32
38
  component?: typeof Store | Store;
39
+ /** 当前路由的元信息(从 RouteRecord 同步) */
40
+ meta?: Record<string, any>;
41
+ /** 当前路由的布局组件(从 RouteRecord 同步) */
42
+ layout?: typeof Store | (() => Promise<{
43
+ default: typeof Store;
44
+ } | typeof Store>);
33
45
  };
34
46
  /** 目录嵌套菜单 */
35
47
  type Menu = {
@@ -41,6 +53,8 @@ type Menu = {
41
53
  hasComponent: boolean;
42
54
  /** 子菜单 */
43
55
  children?: Menu[];
56
+ /** 路由元信息(从 index 文件的 export const routeMeta 提取) */
57
+ meta?: Record<string, any>;
44
58
  };
45
59
  /** 路由匹配结果 */
46
60
  type MatchResult = {
@@ -62,7 +76,7 @@ type GuardResult = boolean | {
62
76
  declare function match(path: string, map: RouteMap): MatchResult | null;
63
77
 
64
78
  /** 创建带初始数据的 RouteRecord */
65
- declare function createRouteRecord(opts?: Partial<Pick<RouteRecord, 'import' | 'component' | 'params'>>): RouteRecord;
79
+ declare function createRouteRecord(opts?: Partial<Pick<RouteRecord, 'import' | 'component' | 'params' | 'meta' | 'layout'>>): RouteRecord;
66
80
  declare class Router extends Store {
67
81
  #private;
68
82
  static [StoreIgnoreKeys]: string[];
package/dist/index.umd.js CHANGED
@@ -68,7 +68,9 @@
68
68
  import: opts.import,
69
69
  component: opts.component,
70
70
  status: opts.component ? 'loaded' : 'idle',
71
- params: opts.params
71
+ params: opts.params,
72
+ meta: opts.meta,
73
+ layout: opts.layout
72
74
  };
73
75
  }
74
76
  class Router extends aoye.Store {
@@ -117,7 +119,9 @@
117
119
  this.active = {
118
120
  path,
119
121
  params: result.params,
120
- component: route?.component
122
+ component: route?.component,
123
+ meta: route?.meta,
124
+ layout: route?.layout
121
125
  };
122
126
  }
123
127
  this.stack = [{
@@ -209,7 +213,10 @@
209
213
  }
210
214
  await this.#loadComponent(target.path);
211
215
  if (id !== this.navId) return;
212
- target.component = this.routes[target.path]?.component;
216
+ const route = this.routes[target.path];
217
+ target.component = route?.component;
218
+ target.meta = route?.meta;
219
+ target.layout = route?.layout;
213
220
  this.active = target;
214
221
  const hash = new URL(target.path, location.origin).hash;
215
222
  if (hash) {
@@ -251,6 +258,12 @@
251
258
  const Comp = mod.default || mod;
252
259
  route.status = 'loaded';
253
260
  route.component = Comp;
261
+ if (!route.meta && mod.routeMeta) {
262
+ route.meta = mod.routeMeta;
263
+ }
264
+ if (!route.layout && mod.layout) {
265
+ route.layout = mod.layout;
266
+ }
254
267
  return route.component;
255
268
  }).catch(err => {
256
269
  route.status = 'error';
@@ -302,9 +315,12 @@
302
315
  }
303
316
  } else {
304
317
  const result = match(current, this.routes);
318
+ const route = result ? this.routes[result.path] : undefined;
305
319
  const entry = {
306
320
  path: current,
307
- params: result?.params ?? {}
321
+ params: result?.params ?? {},
322
+ meta: route?.meta,
323
+ layout: route?.layout
308
324
  };
309
325
  this.stack = [entry];
310
326
  this.stackIndex = 0;