Skip to content

Router 路由 ​

@vital-design/router 是面向 uni-app 的路由库:以 pages.json 为唯一数据源,按「包名(pack)+ 路径(path)」跳转,提供 Vue Router 风格的 API。

优势 ​

  • 异步导航守卫与响应式路由信息:beforeEach / afterEach 支持 async 与 Promise;useRoute / usePageRoute 返回响应式对象,可直接在模板与计算属性中使用。
  • 页面 setup 内即可获取完整页面配置:meta / style / config 等来自 pages.json 的字段在进入页面时即可读取,无需另行查表。
  • 非首屏页面 setup 内无需等待 onLoad 即可拿到完整路由信息(含 query):经 router.push / replace 等跳转进入时,目标页在 beforeCreate / setup 阶段即可读到完整 query。
  • 类型提示与互绑校验:跳转时支持完整路径(如 /pagesBasic/button)与 pack + path / name 的字面量提示;分包与页面互绑校验,静态跳转与守卫重定向共享同一套位置类型。
  • pages.json 唯一数据源:Vite 插件自动读取并注入,应用侧无需手工维护路由表;跳转以 pack + path(或 name / 完整 url)定位,与分包结构对齐。
  • 跳转 API 均为 Promise:失败(页面不存在、参数非法等)时 reject,可用 try/catch 或 .catch 统一处理。
  • 全局路由与页面快照分离:useRoute() 跟踪当前活跃页;usePageRoute() 固定本页信息,跳转后不被新页覆盖,适合页面内逻辑与布局子组件。

安装与接入 ​

TIP

组件包解压安装时,将 @vital-design/router 一并放入项目 node_modules/@vital-design/ 下(同 快速上手 的引入方式)。

1. 注册 Vite 插件 ​

ts
// vite.config.ts
import { VitePluginRouterPacks } from '@vital-design/router/vite';

export default defineConfig({
  plugins: [
    UniPages({ /* ... */ }),
    // 需注册在会自动生成 pages.json 的插件(如 @uni-helper/vite-plugin-uni-pages 等)之后,
    // 以便读取到最新的 pages.json
    VitePluginRouterPacks(),
  ],
});

注册后插件自动完成两件事:读取 src/pages.json 注入给路由包(应用侧无需传参);在项目根目录生成 router-packs.d.ts,让跳转时的包名有字面量提示。推荐把 router-packs.d.ts 加入 .gitignore 与 tsconfig include,dev 下 pages.json 变化会自动重新生成。

2. 创建路由实例 ​

ts
// src/router/index.ts
import { createRouter } from '@vital-design/router';

export const router = createRouter();
ts
// main.ts
import { router } from './router';

app.use(router); // 注入 $router / $route,并自动同步当前页路由

3. 在页面中使用 ​

vue
<script setup lang="ts">
// 可经 unplugin-auto-import 自动导入 useRouter / useRoute / usePageRoute
const router = useRouter();
const route = useRoute();         // 全局当前路由
const pageRoute = usePageRoute(); // 本页路由快照(跳转后不变)

router.push({ pack: 'basic', path: '/button', query: { id: '1' } });
</script>

跳转 ​

方法说明
router.push(location)保留当前页面跳转
router.pushTab(location)跳转 tabBar 页(自动剥离参数)
router.replace(location)关闭当前页跳转
router.replaceAll(location)关闭所有页打开目标页
router.back(delta?)返回上 N 级页面,默认 1

location 支持三种写法:

ts
// ① 完整 url(补全 pages.json 路径,如 `/pagesBasic/button`;带 query 仍合法)
router.push('/pagesBasic/button?id=1');

// ② 按名称(pages.json 的 name);未写 pack 时仅提示主包 name,分包须带 pack(运行时仍可按名定位)
router.push({ name: 'index', query: { id: '1' } });
router.push({ pack: 'basic', name: 'button' });

// ③ 按包名 + 路径(pack 默认 main)
router.push({ path: '/button', pack: 'basic', query: { id: '1' } });

均为 Promise,跳转失败(页面不存在、参数非法等)时 reject,可 try/catch 或 .catch 处理。

获取路由信息 ​

useRoute() — 全局当前路由 ​

返回全局响应式单例(等价于 router.route),始终跟踪最新活跃页。适合布局、全局状态等需要感知「当前在哪个页面」的场景。

usePageRoute() — 页面级路由快照 ​

返回本页独立的响应式快照:只在该页进入时写入,跳转后不会被改成新页。布局内子组件会向上查到所属页面的同一快照;不在页面树内时退回全局 route。

经 router.push / replace 等跳转时,目标页在 beforeCreate / setup 即可读到完整 query(来自跳转入参,不必等小程序 onLoad)。冷启动、直达链接、原生返回等非 router 进入时,仍可能在 onLoad 入参到达后补齐。

选择建议

  • 需要实时跟踪当前活跃页(对齐 Vue Router) → useRoute()
  • 页面内获取自身路由信息(跳转后不变) → usePageRoute()

路由字段 ​

两个 API 返回的对象包含相同字段:

字段说明
route.name路由名称(pages.json name 或路径推导)
route.path包内路径,如 /button
route.pack所在包名
route.fullPath完整路径(含参数),统一以 / 开头,如 /pages/index/index?a=1
route.query当前页参数
route.meta / route.stylepages.json 对应条目字段
route.configpages.json 原始条目(含自定义字段)

导航守卫 ​

以返回值控制导航:

ts
router.beforeEach((to, from) => {
  if (!isLogin.value && to.name !== 'login') {
    return { name: 'login' }; // 重定向
  }
  // 无返回值 / return true → 放行
});

router.beforeEach(async (to) => {
  try {
    await showDialog({
      message: '您还未登录,是否前往登录?',
      showCancelButton: true,
    });

    return { name: 'login' }; // 确认
  } catch {
    return false; // 取消;抛错 / Promise reject 同样取消
  }
});

router.afterEach((to, from) => {
  // 导航完成后上报、埋点等
});
返回值行为
undefined / true / 无返回放行
false取消本次跳转
字符串或位置对象重定向到该位置

守卫按注册顺序依次执行。冷启动、直接输入地址打开、页面返回 / 前进同样触发守卫,此时无上一条路由则 from 为 undefined;入口被重定向时按 replace 跳转,页面已展示时返回 false 仅告警(router.back() 的返回可被拦截)。

包名与根目录配置 ​

默认约定:分包根目录去掉主包根目录(pages)前缀即包名,如 pagesBasic → basic、pagesCase → case。不符合约定时,用插件参数纠正:

ts
VitePluginRouterPacks({
  main: 'pages',                      // 主包根目录不是 pages 时改这里
  pattern: /^pages([A-Z][a-z]+)/,     // 自定义派生,取捕获组 1 作包名
  packs: { case: 'pagesCase' },       // 显式指定 包名 → 根目录
})

配置后跳转即可写 { pack: 'case', path: '/x' },url 自动拼接为 /pagesCase/x。

类型提示 ​

注册插件后 router-packs.d.ts 自动生成 PackRegistry 与 RouteRegistry(含 fullPath)。跳转 API(push / pushTab / replace / replaceAll / resolve)与守卫重定向共用同一套互绑位置类型;push 等另有入参重载以默认仅提示主包:

  • 字符串 url:RouteUrl 用 StringSuggest 提示全部已登记完整路径(如 /pages/index/index、/pagesBasic/button),仍接受任意 string(含 query)
  • 未写 pack:path / name 只提示主包,且二者须对应同一条路由(如 { path: '/index', name: 'case' } 会报错)
  • 写 pack:提示全部分包名,并收窄到该包的 path / name(同样互绑)
  • 动态字段:as RouteLocation(或先写成 RouteLocationLoose 再断言);动态拼接的 url 本身就是宽 string,可直接传入

手动传 packs 时可用泛型补充:

ts
const router = createRouter<'basic' | 'case'>({ ... });
useRouter<'basic' | 'case'>();
useRoute<'basic' | 'case'>();
usePageRoute<'basic' | 'case'>();

动态 path / pack ​

kebabCase() 等返回的是宽 string,模板字符串 `/${kebabCase(name)}` 也是 string,对不上登记表里的字面量,类型会报错。这是预期行为:互绑检查只对静态字面量生效。

动态跳转时任选其一即可:

ts
import type { RouteLocation } from '@vital-design/router';

// ① 整对象断言(推荐)
router.push({
  path: `/${kebabCase(name)}`,
  pack: kebabCase(nav),
} as RouteLocation);

// ② 完整 url
router.push(`/pagesBasic/${kebabCase(name)}`);

守卫重定向直接返回位置对象(与 push 同一套互绑类型)。返回值位无法像 push 入参那样「默认仅主包」:未写 pack 时 path 为全部登记路径;先写 pack 再写 path 即可收窄。

ts
router.beforeEach((to) => {
  if (!to.name) return { path: '/index' };
  return { pack: 'basic', path: '/button' }; // 建议先写 pack
});

静态跳转继续写字面量,保留完整类型检查:

ts
router.push({ pack: 'basic', path: '/button' });

createRouter 入参(不走 vite 插件时) ​

ts
import pagesData from '@/pages.json';

const router = createRouter<'basic' | 'case'>({
  pagesData,
  packs: { case: 'pagesCase' },
  main: 'pages',
});
参数说明类型默认值
pagesDatapages.json 数据PagesData-
packs包映射表:包名 → 页面根目录Partial<Record<P, string>>{}
main主包根目录string'pages'

配合 CLI 生成类型声明:

bash
npx vital-router-gen src/pages.json -o router-packs.d.ts --main pages --pack case=pagesCase

VitePluginRouterPacks 配置项 ​

参数说明类型默认值
pagesJsonpages.json 路径(相对项目根目录)string'src/pages.json'
dtsPackRegistry 声明文件输出路径string'router-packs.d.ts'
main主包根目录string'pages'
pattern分包名派生正则RegExp | string-
packs显式包映射表Record<string, string>{}

已知边界 ​

  • pushTab 目标页不带 query(平台限制,自动剥参数)。
  • 跳转的路径未命中 pages.json 记录时按「包根目录 + path + /index」兜底并告警,请确保路径已在 pages.json 登记。
  • 多段子包根目录(如 pagesBasic/button)默认派生包名为 basic/button,想要单层包名请配 pattern 或 packs。

赣ICP备2025061025号-1