Skip to content

RouteMeta

路由元信息,用于描述路由的附加属性。通过 RouteConfig.meta 配置,在守卫、afterEach 钩子、组件中通过 to.meta / route.meta 访问。

类型定义

ts
interface RouteMeta {
  title?: string
  isTab?: boolean
  requireAuth?: boolean
  animation?: NavigationAnimation
  [key: string]: any
}

内置属性

title

  • 类型: string | undefined
  • 说明: 页面标题,可在 afterEach 钩子中用于设置导航栏标题
ts
const routes = [
  { path: 'pages/index/index', name: 'home', meta: { title: '首页' } },
  { path: 'pages/about/about', name: 'about', meta: { title: '关于我们' } }
]

// afterEach 中自动设置标题
router.afterEach((to) => {
  if (to.meta.title) {
    uni.setNavigationBarTitle({ title: to.meta.title as string })
  }
})

isTab

  • 类型: boolean | undefined
  • 说明: 是否为 TabBar 页面。路由器根据此字段自动选择导航 API
    • trueuni.switchTab
    • false / 未设置 → uni.navigateTo / uni.redirectTo

必须与 pages.json 一致

meta.isTab 必须与 pages.jsontabBar.list 声明一致。如果声明了 isTab: true 但不在 tabBar.list 中,uni.switchTab 会失败;反之亦然。

建议使用 @meng-xi/vite-plugin 自动生成路由配置,会自动识别 TabBar 页面并设置 isTab

ts
const routes = [
  { path: 'pages/index/index', name: 'home', meta: { isTab: true } },   // TabBar 页面
  { path: 'pages/about/about', name: 'about', meta: { isTab: false } }  // 普通页面
]

requireAuth

  • 类型: boolean | undefined
  • 说明: 是否需要登录认证,常与 beforeEach 守卫配合使用
ts
const routes = [
  { path: 'pages/index/index', name: 'home', meta: { requireAuth: false } },
  { path: 'pages/about/about', name: 'about', meta: { requireAuth: true } },
  { path: 'pages/user/user', name: 'user', meta: { requireAuth: true } }
]

// 全局前置守卫
router.beforeEach((to, from) => {
  const isLoggedIn = !!uni.getStorageSync('token')

  if (to.meta.requireAuth && !isLoggedIn) {
    return { name: 'login', query: { redirect: to.fullPath } }
  } else {
    return
  }
})

animation

  • 类型: NavigationAnimation | undefined
  • 说明: 默认导航动画,可被 push / replace / back 调用时传入的 animation 参数覆盖。App 端为原生窗口动画,H5 端由本库通过 CSS 过渡实现(仅 push / back 生效),小程序自动忽略
ts
interface NavigationAnimation {
  type: UniAnimationType
  duration?: number // 默认 300ms
}

动画优先级:调用时传入 > meta.animation > uni 默认值

ts
const routes = [
  {
    path: 'pages/about/about',
    name: 'about',
    meta: { animation: { type: 'fade-in', duration: 300 } }
  },
  {
    path: 'pages/detail/detail',
    name: 'detail',
    meta: { animation: { type: 'slide-in-bottom', duration: 500 } }
  }
]

// 使用 meta 中的默认动画
await router.push({ name: 'about' }) // fade-in, 300ms

// 调用时覆盖
await router.push({ name: 'about', animation: { type: 'slide-in-right' } }) // slide-in-right, 300ms

平台限制

动画在 App 端为原生窗口动画(所有导航方式生效),H5 端通过 CSS 过渡实现(仅 push / back 生效,replace / relaunch 无动画),小程序端由宿主控制、无法自定义。

UniAnimationType 可选值(显示动画 navigateTo 与关闭动画 navigateBack):

  • 显示动画:'slide-in-right' / 'slide-in-left' / 'slide-in-top' / 'slide-in-bottom' / 'pop-in' / 'fade-in' / 'zoom-out' / 'zoom-fade-out'
  • 关闭动画:'slide-out-right' / 'slide-out-left' / 'slide-out-top' / 'slide-out-bottom' / 'pop-out' / 'fade-out' / 'zoom-in' / 'zoom-fade-in'
  • 通用:'none'(无动画)/ 'auto'(自动选择)

动画类型本身没有"默认值",仅 duration 默认 300ms;如未指定 typepush / back 使用 uni 的默认过渡。

自定义扩展

通过索引签名 [key: string]: any 支持任意自定义字段:

ts
const routes = [
  {
    path: 'pages/article/detail',
    name: 'article-detail',
    meta: {
      title: '文章详情',
      requireAuth: false,
      keepAlive: true,              // 自定义:是否缓存
      permissions: ['read', 'comment'],  // 自定义:所需权限
      layout: 'default',            // 自定义:布局类型
      breadcrumb: ['首页', '文章', '详情']  // 自定义:面包屑
    }
  }
]

类型增强(推荐)

通过 TypeScript 的声明合并为 RouteMeta 添加类型提示:

ts
// src/types/router.d.ts
declare module '@meng-xi/uni-router' {
  interface RouteMeta {
    // 自定义字段
    keepAlive?: boolean
    permissions?: string[]
    layout?: 'default' | 'simple' | 'full'
    breadcrumb?: string[]
    // 业务字段
    roles?: string[]        // 所需角色
    module?: string         // 所属模块
    cacheKey?: string       // 缓存键
  }
}

增强后,在路由配置和守卫中访问自定义字段会有类型提示:

ts
const routes: RouteConfig[] = [
  {
    path: 'pages/admin/admin',
    name: 'admin',
    meta: {
      title: '管理后台',
      requireAuth: true,
      roles: ['admin', 'superadmin'],  // ✅ 有类型提示
      module: 'admin'                  // ✅ 有类型提示
    }
  }
]

// 守卫中访问
router.beforeEach((to, from) => {
  if (to.meta.roles) {
    const userRoles = getUserRoles()
    if (!to.meta.roles.some(r => userRoles.includes(r))) {
      return { name: '403' }
    }
  }
  return
})

类型增强的好处

  • 类型安全:拼写错误会在编译时报错
  • 自动补全:IDE 会提示可用的字段
  • 文档化:团队成员能直观看到支持哪些 meta 字段

实战应用

权限控制

ts
// 扩展类型
declare module '@meng-xi/uni-router' {
  interface RouteMeta {
    roles?: string[]
    permissions?: string[]
  }
}

const routes: RouteConfig[] = [
  {
    path: 'pages/admin/admin',
    name: 'admin',
    meta: {
      title: '管理后台',
      requireAuth: true,
      roles: ['admin'],
      permissions: ['read', 'write', 'delete']
    }
  }
]

router.beforeEach((to, from) => {
  if (to.meta.roles && !hasRole(to.meta.roles)) {
    return { name: '403' }
  }
  if (to.meta.permissions && !hasPermission(to.meta.permissions)) {
    return { name: '403' }
  }
  return
})

动态标题

ts
const routes = [
  { path: 'pages/index/index', name: 'home', meta: { title: '首页' } },
  { path: 'pages/about/about', name: 'about', meta: { title: '关于我们' } },
  { path: 'pages/user/user', name: 'user', meta: { title: '个人中心' } }
]

// afterEach 中统一设置标题
router.afterEach((to) => {
  const title = to.meta.title as string | undefined
  uni.setNavigationBarTitle({ title: title || '默认标题' })
})

页面缓存控制

ts
declare module '@meng-xi/uni-router' {
  interface RouteMeta {
    keepAlive?: boolean
    cacheKey?: string
  }
}

const routes = [
  {
    path: 'pages/list/list',
    name: 'list',
    meta: {
      keepAlive: true,
      cacheKey: 'list-data'  // 缓存键
    }
  }
]

// 在页面中根据 meta 决定是否缓存
const route = useRoute()
const shouldCache = computed(() => route.value.meta.keepAlive === true)

布局切换

ts
declare module '@meng-xi/uni-router' {
  interface RouteMeta {
    layout?: 'default' | 'simple' | 'full'
  }
}

const routes = [
  { path: 'pages/index/index', name: 'home', meta: { layout: 'default' } },
  { path: 'pages/login/login', name: 'login', meta: { layout: 'simple' } },
  { path: 'pages/editor/editor', name: 'editor', meta: { layout: 'full' } }
]

// App.vue 中根据 meta 切换布局
const route = useRoute()
const currentLayout = computed(() => route.value.meta.layout || 'default')

埋点上报

ts
declare module '@meng-xi/uni-router' {
  interface RouteMeta {
    trackName?: string  // 埋点页面名
    trackParams?: Record<string, any>  // 埋点参数
  }
}

const routes = [
  {
    path: 'pages/detail/detail',
    name: 'detail',
    meta: {
      trackName: 'detail_page',
      trackParams: { source: 'home' }
    }
  }
]

router.afterEach((to) => {
  if (to.meta.trackName) {
    trackPageView(to.meta.trackName, to.meta.trackParams)
  }
})

下一步

Released under the MIT License.