本文基于 v1.5.0 版本,全面介绍 @meng-xi/uni-router 的设计理念与完整功能。
为什么需要 uni-router?
uni-app 原生路由系统基于 pages.json 静态配置,导航通过 uni.navigateTo / uni.redirectTo / uni.reLaunch / uni.navigateBack 等 API 直接调用,缺少以下关键能力:
- 无路由守卫:无法在导航前进行权限校验、登录检查等拦截
- 无命名路由:必须硬编码路径字符串,重构时容易遗漏
- 无路由元信息:无法为路由附加标题、权限标记等结构化数据
- 无错误体系:导航失败时只能通过回调获取,缺乏统一的错误处理机制
- 无状态同步:浏览器后退、物理返回键等场景下路由状态可能不一致
- 无页面间通信:无法在导航时建立页面间的事件通道
@meng-xi/uni-router 的目标是在 uni-app 的静态页面模型上,提供一套 vue-router 风格的路由管理方案,让开发者在 uni-app 中也能享受现代化的路由开发体验。
核心设计理念
不替代 pages.json,而是与之配合
uni-router 不替代 pages.json。页面注册仍由 pages.json 负责,uni-router 在此基础上提供路由导航、守卫、元信息等增强能力。这种设计确保了:
- 完全兼容 uni-app 的页面管理机制
- 不影响
pages.json的条件编译等原生能力 - 可以渐进式引入,无需改造现有项目
基于 uni-app 原生 API 实现
所有导航操作最终通过 uni.navigateTo / uni.redirectTo / uni.switchTab / uni.reLaunch / uni.navigateBack 执行,不绕过 uni-app 的页面管理机制,确保跨平台行为一致。
功能全景
一、路由导航
push — 导航到新页面
// 路径字符串
await router.push('/pages/about/about')// 路径对象 + 查询参数
await router.push({ path: '/pages/about/about', query: { id: '1' } })// 命名路由
await router.push({ name: 'about' })// 带动画参数(仅 App 端生效)
await router.push({ path: '/pages/about/about', animation: { type: 'slide-in-bottom', duration: 500 } })// 带页面间通信
const result = await router.push({path: '/pages/about/about',events: {// 监听目标页面通过 eventChannel.emit 发送的事件receiveData: data => {console.log('收到数据:', data)}}
})
// 通过 eventChannel 向目标页面发送事件
result.eventChannel?.emit('fromOpener', { msg: '你好' })
push 自动根据 meta.isTab 选择 uni.navigateTo(普通页面)或 uni.switchTab(TabBar 页面),开发者无需手动判断。
push 返回 NavigationResult,包含目标路由位置和可选的 eventChannel,用于页面间双向通信。
replace — 替换当前页面
await router.replace('/pages/login/login')
await router.replace({ name: 'home' })
对应 uni.redirectTo。替换 TabBar 页面时自动切换为 uni.switchTab(会关闭所有非 Tab 页面)。
relaunch — 关闭所有页面并打开目标页面
// 路径字符串
await router.relaunch('/pages/index/index')// 命名路由 + 查询参数
await router.relaunch({ name: 'login', query: { redirect: '/about' } })
对应 uni.reLaunch,常用于:
- 退出登录后跳转登录页
- 从深层页面返回首页
- 重置整个页面栈
设计细节:
- TabBar 页面自动切换为
uni.switchTab uni.reLaunch不支持动画参数,传入时输出警告- 不进行重复导航检测(清栈场景下目标页面可能就是当前页面)
- 走完整守卫链(beforeEach → beforeEnter → beforeResolve → afterEach)
back — 返回上一页
await router.back() // 返回上一页
await router.back(2) // 返回两级
await router.back(1, { type: 'slide-out-left' }) // 指定动画
back() 执行完整的守卫链,守卫可以中止或重定向返回操作。若未指定动画参数,将使用目标页面的 meta.animation 作为默认动画。
back() 返回 Promise<RouteLocation>,即返回到的目标页面路由位置,调用者可获取返回后的页面信息:
const targetRoute = await router.back(2)
console.log('返回到的页面:', targetRoute.path)
console.log('页面参数:', targetRoute.query)
重复导航检测
push 到当前页面时自动拒绝并抛出 NAVIGATION_DUPLICATED 错误,避免重复入栈。
注意:
relaunch不进行重复导航检测。因为清栈场景下目标页面可能就是当前页面(如"返回首页"),拒绝此类导航没有意义。
并发导航排队
多次并发导航自动排队,前一次完成后再执行下一次,避免页面栈混乱。
二、页面间通信(EventChannel)
基于 uni.navigateTo 的 EventChannel 机制,实现页面间双向通信。
什么是 EventChannel?
EventChannel 是 uni-app 提供的页面间通信通道,允许发起导航的页面和被打开的页面通过事件机制互相发送数据。这在以下场景中非常有用:
- 打开详情页时传递初始数据
- 子页面操作完成后通知父页面刷新
- 表单页提交结果回传到列表页
编程式导航通信
发起页 — 通过 push 的 events 参数和返回值通信
// 发起页
const result = await router.push({path: '/pages/detail/detail',query: { id: '1' },events: {// 监听目标页面 emit 的事件acceptData: data => {console.log('收到详情页数据:', data)},// 监听详情页的编辑完成通知editComplete: data => {console.log('编辑完成:', data)// 刷新列表等操作}}
})// 通过 eventChannel 向目标页面发送事件
result.eventChannel?.emit('initData', { msg: '来自发起页的初始数据' })
目标页 — 通过 getOpenerEventChannel 接收和回复
// 目标页(detail)
export default {onLoad() {const eventChannel = this.getOpenerEventChannel()// 监听发起页发来的事件eventChannel.on('initData', data => {console.log('收到初始数据:', data)})// 向发起页发送数据eventChannel.emit('acceptData', { msg: '详情页已加载' })},methods: {onSave() {const eventChannel = this.getOpenerEventChannel()// 通知发起页编辑完成eventChannel.emit('editComplete', { id: 1, action: 'saved' })}}
}
声明式导航通信(RouterLink)
<template><mxuni-router :to="{ path: '/pages/detail/detail', query: { id: '1' } }" :events="{ acceptData: data => onAcceptData(data) }" @navigated="onNavigated" @error="onNavError"><view>跳转到详情页</view></mxuni-router>
</template><script setup>
function onAcceptData(data) {console.log('收到详情页数据:', data)
}function onNavigated(eventChannel) {// 导航成功后,通过 eventChannel 向目标页面发送事件eventChannel?.emit('initData', { msg: '来自 RouterLink 的初始数据' })
}function onNavError(error) {console.log('导航失败:', error.code)
}
</script>
EventChannel 接口
interface EventChannel {/** 监听事件,每次 emit 都会触发 */on(event: string, callback: (...args: any[]) => void): EventChannel/** 监听事件,仅触发一次后自动移除 */once(event: string, callback: (...args: any[]) => void): EventChannel/** 取消监听事件 */off(event: string, callback?: (...args: any[]) => void): EventChannel/** 触发事件 */emit(event: string, ...args: any[]): EventChannel
}
通信限制
| 导航方式 | 对应 uni API | 支持 EventChannel | 说明 |
|---|---|---|---|
push |
uni.navigateTo |
✅ | 返回 NavigationResult.eventChannel |
replace |
uni.redirectTo |
❌ | 传入 events 时输出警告并忽略 |
relaunch |
uni.reLaunch |
❌ | 传入 events 时输出警告并忽略 |
back |
uni.navigateBack |
❌ | 返回操作不创建新页面,无通信通道 |
| TabBar 页面 | uni.switchTab |
❌ | 传入 events 时输出警告并忽略 |
三、路由守卫
路由守卫是 uni-router 最核心的能力,提供完整的导航拦截机制。
全局前置守卫 — beforeEach
在每次导航前执行,常用于登录验证:
router.beforeEach((to, from, next) => {if (to.meta.requireAuth && !isLoggedIn()) {next({ name: 'login', query: { redirect: to.fullPath } })} else {next()}
})
全局解析守卫 — beforeResolve
在所有前置守卫和路由独享守卫完成后执行,适合需要确保所有守卫都已通过的场景:
router.beforeResolve((to, from, next) => {// 所有前置守卫已通过,导航即将执行next()
})
全局后置钩子 — afterEach
在导航完成后执行,不影响导航结果,适合埋点、标题设置等操作:
router.afterEach((to, from) => {if (to.meta.title) {uni.setNavigationBarTitle({ title: to.meta.title as string })}
})
路由独享守卫 — beforeEnter
在路由配置中定义,仅对该路由生效:
const routes = [{path: 'pages/admin/admin',name: 'admin',meta: { requireAuth: true },beforeEnter: (to, from, next) => {if (isAdmin()) next()else next({ name: 'forbidden' })}}
]
支持数组形式,按顺序依次执行:
const routes = [{path: 'pages/admin/admin',name: 'admin',beforeEnter: [checkAuth, checkAdmin]}
]
守卫执行顺序
beforeEach → beforeEnter → beforeResolve → 导航执行 → afterEach
守卫重定向
守卫中调用 next(location) 可重定向到其他路由,支持多级重定向(最大深度 10):
router.beforeEach((to, from, next) => {if (to.meta.requireAuth && !isLoggedIn()) {next({ name: 'login' }) // 重定向到登录页} else {next()}
})
守卫超时保护
通过 guardTimeout 配置项(默认 10000ms),防止守卫未调用 next() 导致导航永久挂起:
const router = createRouter({routes,guardTimeout: 15000 // 15 秒超时
})
守卫注册返回移除函数
所有守卫注册函数均返回一个移除函数,支持动态注册和注销:
const removeGuard = router.beforeEach((to, from, next) => {// 临时守卫逻辑next()
})// 不再需要时移除
removeGuard()
四、命名路由
通过 name 字段进行导航,避免硬编码路径字符串:
// 路由配置
const routes = [{ path: 'pages/index/index', name: 'home', meta: { isTab: true } },{ path: 'pages/about/about', name: 'about', meta: { title: '关于' } }
]// 导航时使用名称
await router.push({ name: 'about', query: { id: '1' } })
配合 @meng-xi/vite-plugin 自动生成的类型声明,路由名称可获得 TypeScript 自动补全和类型检查。
五、路由元信息
meta 字段支持页面标题、权限标记、TabBar 标识、导航动画等自定义数据:
interface RouteMeta {title?: string // 页面标题isTab?: boolean // 是否为 TabBar 页面requireAuth?: boolean // 是否需要登录认证animation?: NavigationAnimation // 默认导航动画(仅 App 端)[key: string]: any // 自定义扩展字段
}
使用示例:
const routes = [{ path: 'pages/index/index', name: 'home', meta: { isTab: true, title: '首页' } },{ path: 'pages/about/about', name: 'about', meta: { animation: { type: 'fade-in' } } },{ path: 'pages/admin/admin', name: 'admin', meta: { requireAuth: true } }
]
注意:
[key: string]索引签名类型为any,允许自由扩展自定义字段。v1.4.0 及之前版本文档中误写为unknown,v1.5.0 已修正。
六、导航动画
完整的页面切换动画支持,仅 App 端生效,其他平台自动忽略。
NavigationAnimation 接口
interface NavigationAnimation {type: UniAnimationType // 动画类型duration?: number // 持续时间(ms),默认 300
}
UniAnimationType — 完整动画类型
| 显示动画(navigateTo) | 关闭动画(navigateBack) |
|---|---|
slide-in-right |
slide-out-right |
slide-in-left |
slide-out-left |
slide-in-top |
slide-out-top |
slide-in-bottom |
slide-out-bottom |
fade-in |
fade-out |
zoom-out |
zoom-in |
zoom-fade-out |
zoom-fade-in |
pop-in |
pop-out |
auto / none |
auto / none |
三种使用方式
1. 导航时传入动画参数
await router.push({ path: '/pages/about/about', animation: { type: 'slide-in-bottom' } })
await router.back(1, { type: 'slide-out-left', duration: 500 })
2. 路由级默认动画(meta.animation)
const routes = [{ path: 'pages/about/about', name: 'about', meta: { animation: { type: 'fade-in' } } }]
3. RouterLink 声明式动画
<RouterLink to="/pages/about/about" :animation="{ type: 'slide-in-bottom' }">底部滑入
</RouterLink>
动画优先级
push/replace/back 调用时传入 > meta.animation > uni 默认值
各导航方式对动画的支持
| 导航方式 | 对应 uni API | 支持动画 | 说明 |
|---|---|---|---|
push |
uni.navigateTo |
✅ | 传入 animationType/duration |
replace |
uni.redirectTo |
❌ | uni.redirectTo 不支持动画参数 |
relaunch |
uni.reLaunch |
❌ | uni.reLaunch 不支持动画参数 |
back |
uni.navigateBack |
✅ | 传入 animationType/duration |
| TabBar 页面 | uni.switchTab |
❌ | uni.switchTab 不支持动画参数 |
传入动画参数但不支持时,路由器会输出 console.warn 提醒开发者。
默认动画持续时间常量
v1.5.0 新增导出常量 DEFAULT_ANIMATION_DURATION,值为 300,与 uni-app 官方默认值一致:
import { DEFAULT_ANIMATION_DURATION } from '@meng-xi/uni-router'console.log(DEFAULT_ANIMATION_DURATION) // 300
七、uni API 拦截
通过 interceptUniApi 选项拦截原生导航 API,确保路由守卫始终生效:
const router = createRouter({routes,interceptUniApi: true // 拦截 uni.navigateTo 等原生 API
})
启用后,以下调用将被拦截并转由路由器处理:
// 这两种方式等价,都会经过守卫链
uni.navigateTo({ url: '/pages/about/about' })
router.push('/pages/about/about')
拦截原理
- 通过
uni.addInterceptor注册拦截器 - 路由器内部发起的 API 调用通过计数器标记放行,避免重复执行守卫
- 外部调用被拦截后,阻止原始 API 执行,转由
router.push/replace/relaunch/back执行完整守卫链 - 低版本小程序基础库兼容:修改
args.url为空字符串,防止忽略返回值继续执行
拦截范围
| API | 拦截后行为 |
|---|---|
uni.navigateTo |
→ router.push |
uni.redirectTo |
→ router.replace |
uni.switchTab |
→ router.push |
uni.reLaunch |
→ router.relaunch |
uni.navigateBack |
→ router.back |
v1.5.0 明确文档:
interceptUniApi拦截全部 5 个 uni 导航 API(含uni.reLaunch),早期版本文档中遗漏了reLaunch。
八、组合式 API
useRouter — 获取路由器实例
import { useRouter } from '@meng-xi/uni-router'const router = useRouter()
await router.push('/pages/about/about')
必须在 Vue 组件的 setup() 中调用,通过 Vue 的 inject 机制获取路由器实例。
useRoute — 获取响应式路由位置
import { useRoute } from '@meng-xi/uni-router'const route = useRoute()
// route 是 Ref<RouteLocation>,路由变化时自动更新
console.log(route.value.path)
console.log(route.value.query)
同一 router 实例共享同一个响应式 ref,通过 WeakMap 缓存避免重复创建。
Options API — $router 和 $route
在 Options API 组件中,可通过 this.$router 和 this.$route 访问路由器:
export default {methods: {goToAbout() {this.$router.push('/pages/about/about')}},computed: {currentPath() {return this.$route.path}}
}
为避免与 uni-app H5 内置的 vue-router 冲突,
$router和$route仅在未被定义时设置。
九、RouterLink 组件
基于 uni-app navigator 封装的声明式导航组件:
<template><!-- 路径跳转 --><RouterLink to="/pages/about/about">关于页面</RouterLink><!-- 命名路由 + 替换模式 --><RouterLink :to="{ name: 'about' }" replace>替换导航</RouterLink><!-- relaunch 模式(关闭所有页面并打开目标页面) --><RouterLink to="/pages/index/index" relaunch>返回首页</RouterLink><!-- 带动画参数 --><RouterLink to="/pages/about/about" :animation="{ type: 'fade-in' }">淡入动画</RouterLink><!-- 带页面间通信 --><RouterLink :to="{ path: '/pages/detail/detail', query: { id: '1' } }" :events="{ acceptData: data => console.log(data) }" @navigated="onNavigated" @error="onNavError"> 跳转并通信 </RouterLink>
</template><script setup>
import { RouterLink } from '@meng-xi/uni-router/components/RouterLink.vue'function onNavigated(eventChannel) {// 向目标页面发送事件eventChannel?.emit('initData', { msg: '来自 RouterLink' })
}function onNavError(error) {console.log('导航失败:', error.code)
}
</script>
Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
to |
RouteLocationRaw |
- | 目标路由位置 |
replace |
boolean |
false |
是否使用替换模式导航 |
relaunch |
boolean |
false |
是否使用 relaunch 模式导航,优先级高于 replace |
animation |
NavigationAnimation |
undefined |
导航动画(仅 App 端) |
events |
EventListeners |
undefined |
页面间通信事件监听器(仅 push 时生效) |
hoverClass |
string |
'navigator-hover' |
按下时的样式类 |
hoverStopPropagation |
boolean |
false |
阻止祖先节点点击态 |
hoverStartTime |
number |
50 |
按住后出现点击态时间 |
hoverStayTime |
number |
600 |
松开后点击态保留时间 |
Events
| 事件 | 参数 | 说明 |
|---|---|---|
navigated |
EventChannel | void |
导航成功后触发,参数为 eventChannel 实例 |
error |
NavigationFailure |
导航失败时触发 |
十、路由状态同步
当页面通过浏览器后退、物理返回键等非路由器方式切换时,路由器的 currentRoute 可能与实际页面不同步。syncRoute() 方法从 uni-app 页面栈中读取当前页面信息并更新路由状态:
// 在每个页面的 onShow 生命周期中调用
import { onShow } from '@dcloudio/uni-app'
import { useRouter } from '@meng-xi/uni-router'const router = useRouter()onShow(() => {router.syncRoute()
})
onRouteChange — 路由变化监听
注册路由状态变化监听器,导航完成和状态同步时都会触发:
router.onRouteChange((to, from) => {console.log(`路由变化: ${from.path} → ${to.path}`)
})
与 afterEach 不同,onRouteChange 也会捕获 syncRoute() 触发的状态变化。
十一、路由器就绪超时保护(v1.5.0 新增)
v1.5.0 新增 readyTimeout 配置项,解决路由器初始化异常时 isReady() Promise 永久挂起的问题。
问题背景
router.isReady() 返回一个 Promise,在路由器完成初始化后 resolve。正常情况下,路由器在 createRouter() 时就会通过 initRoute() 完成初始化。但在某些异常场景下(如页面栈为空且无法获取当前页面信息),isReady()
可能永远不会 resolve,导致 await router.isReady() 永久阻塞。
readyTimeout 配置
const router = createRouter({routes,readyTimeout: 5000 // 5 秒超时
})
| 值 | 行为 |
|---|---|
0 |
永不超时(默认行为,向后兼容) |
>0 |
超时后 isReady() reject,抛出超时错误 |
使用示例
const router = createRouter({routes,readyTimeout: 5000
})try {await router.isReady()// 路由器初始化完成,可以安全使用
} catch (error) {// 路由器初始化超时,可能存在异常console.error(error.message)// "[uni-router] Router isReady() timed out after 5000ms. The router was not initialized properly."
}
与 guardTimeout 的区别
| 配置项 | 保护对象 | 默认值 | 说明 |
|---|---|---|---|
guardTimeout |
导航守卫 | 10000 |
守卫未调用 next() 时自动中止导航 |
readyTimeout |
路由器初始化 | 0 |
isReady() 超时后 reject,防止永久挂起 |
十二、错误处理
完整的错误体系
// RouterError — 路由错误基类
class RouterError extends Error {readonly code: RouterErrorCode
}// NavigationFailure — 导航失败,包含来源和目标信息
class NavigationFailure extends RouterError {readonly to: RouteLocationreadonly from: RouteLocationreadonly cause?: unknown
}
错误码
| 错误码 | 说明 |
|---|---|
NAVIGATION_ABORTED |
导航被守卫中止或守卫超时 |
NAVIGATION_CANCELLED |
导航被取消(守卫异常或重定向超限) |
NAVIGATION_DUPLICATED |
重复导航到当前位置 |
ROUTE_NOT_FOUND |
未找到匹配的路由 |
NAVIGATION_API_ERROR |
uni 导航 API 调用失败 |
SETUP_ERROR |
路由器初始化或使用方式错误 |
全局错误捕获
router.onError((error, to, from) => {if (error.code === 'NAVIGATION_ABORTED') {console.log('导航被中止')}
})
十三、TypeScript 类型提示
配合 @meng-xi/vite-plugin 自动生成的类型声明,为路由导航提供类型安全:
// 路由名称自动补全
router.push({ name: 'pagesIndexIndex' }) // ✅ 自动补全
router.push({ name: 'invalidName' }) // ❌ 类型错误// 路径自动补全
router.push({ path: '/pages/index/index' }) // ✅ 自动补全
router.push({ path: '/invalid/path' }) // ❌ 类型错误
通过模块增强(module augmentation)填充 RouteNameMap 接口即可启用:
declare module '@meng-xi/uni-router' {interface RouteNameMap {pagesIndexIndex: { path: '/pages/index/index'; meta: { title: string; isTab: true } }pagesAboutAbout: { path: '/pages/about/about'; meta: { title: string } }}
}
API 速查
核心
| API | 说明 |
|---|---|
createRouter(options) |
创建路由器实例 |
useRouter() |
获取路由器实例(组合式 API) |
useRoute() |
获取响应式路由位置(组合式 API) |
RouterLink |
声明式导航组件 |
Router 实例方法
| 方法 | 说明 | 返回值 |
|---|---|---|
router.push(location) |
导航到新页面 | Promise<NavigationResult> |
router.replace(location) |
替换当前页面 | Promise<RouteLocation> |
router.relaunch(location) |
关闭所有页面并打开目标页面 | Promise<RouteLocation> |
router.back(delta?, animation?) |
返回上一页或多级页面 | Promise<RouteLocation> |
router.beforeEach(guard) |
注册全局前置守卫 | () => void |
router.beforeResolve(guard) |
注册全局解析守卫 | () => void |
router.afterEach(guard) |
注册全局后置钩子 | () => void |
router.onRouteChange(fn) |
注册路由变化监听器 | () => void |
router.onError(handler) |
注册错误处理回调 | () => void |
router.isReady() |
等待路由器初始化完成 | Promise<void> |
router.syncRoute() |
同步路由状态与页面栈 | void |
router.resolve(location) |
解析路由位置(不导航) | RouteLocation |
router.getRoutes() |
获取所有路由配置 | RouteConfig[] |
router.hasRoute(name) |
检查路由是否存在 | boolean |
RouterOptions
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
routes |
RouteConfig[] |
- | 路由配置列表 |
strict |
boolean |
true |
严格模式,未匹配路由时抛出异常 |
interceptUniApi |
boolean |
false |
拦截 navigateTo / redirectTo / switchTab / reLaunch / navigateBack 原生导航 API |
guardTimeout |
number |
10000 |
守卫超时时间(ms),设为 0 可禁用 |
readyTimeout |
number |
0 |
路由器就绪超时时间(ms),设为 0 可禁用(v1.5.0 新增) |
导出常量
| 常量 | 值 | 说明 |
|---|---|---|
DEFAULT_ANIMATION_DURATION |
300 |
默认动画持续时间(毫秒),与 uni-app 官方默认值一致 |
与 pages.json 的关系
| 职责 | pages.json | uni-router |
|---|---|---|
| 页面注册 | 必须声明 | 不负责 |
| 路由导航 | uni.navigateTo 等 |
push / replace / relaunch / back |
| 路由守卫 | 不支持 | beforeEach 等 |
| 路由元信息 | 不支持 | meta 字段 |
| 命名路由 | 不支持 | name 字段 |
| 导航动画 | 手动传参 | animation + meta.animation |
快速开始
1. 安装
pnpm add @meng-xi/uni-router
2. 配置路由
// src/router.config.ts
import type { RouteConfig } from '@meng-xi/uni-router'const routes: RouteConfig[] = [{ path: 'pages/index/index', name: 'home', meta: { isTab: true, title: '首页' } },{ path: 'pages/about/about', name: 'about', meta: { title: '关于' } },{ path: 'pages/login/login', name: 'login', meta: { title: '登录' } },{ path: 'pages/admin/admin', name: 'admin', meta: { requireAuth: true } }
]export default routes
3. 创建路由器
// src/main.ts
import { createSSRApp } from 'vue'
import { createRouter } from '@meng-xi/uni-router'
import routes from './router.config'
import App from './App.vue'const router = createRouter({routes,strict: true,interceptUniApi: true,guardTimeout: 15000,readyTimeout: 5000 // v1.5.0: 路由器就绪超时保护
})// 注册全局守卫
router.beforeEach((to, from, next) => {if (to.meta.requireAuth && !isLoggedIn()) {next({ name: 'login' })} else {next()}
})router.afterEach(to => {if (to.meta.title) {uni.setNavigationBarTitle({ title: to.meta.title as string })}
})export function createApp() {const app = createSSRApp(App)app.use(router)return { app }
}
4. 在页面中使用
<template><view><text>当前路径: {{ route.path }}</text><button @click="goAbout">跳转关于</button><button @click="goBack">返回</button></view>
</template><script setup>
import { useRouter, useRoute } from '@meng-xi/uni-router'const router = useRouter()
const route = useRoute()async function goAbout() {try {await router.push({ name: 'about', query: { from: 'home' } })} catch (e) {console.log('导航失败:', e.code)}
}async function goBack() {const target = await router.back()console.log('返回到:', target.path)
}
</script>
v1.5.0 更新摘要
新增
readyTimeout配置项 — 路由器就绪超时保护,防止isReady()Promise 永久挂起isReady()超时 reject — 配置readyTimeout > 0时,超时后自动 reject 并抛出明确错误信息DEFAULT_ANIMATION_DURATION导出常量 — 默认动画持续时间(300ms),与 uni-app 官方默认值一致
修复
interceptUniApi拦截列表文档修正 — 明确文档中列出全部 5 个被拦截的 uni API(含uni.reLaunch),早期版本文档遗漏RouteMeta索引签名类型修正 —[key: string]类型从unknown修正为any,与实际实现保持一致router.back()返回值文档修正 — 返回值类型从Promise<void>修正为Promise<RouteLocation>,与实际实现保持一致
平台兼容性
| 平台 | 支持情况 |
|---|---|
| uni-app (Vue 3) | ✅ 完全支持 |
| uni-app H5 (Safari / Chrome) | ✅ 完全支持 |
| uni-app App (Android / iOS) | ✅ 完全支持(动画仅 App 端生效) |
| uni-app 小程序(微信/支付宝/百度/字节等) | ✅ 完全支持 |
| uni-app x Web | ✅ 支持 |
| uni-app x 小程序 | ✅ 支持 |
| uni-app x App (Android / iOS / Harmony) | ❌ 不支持(无 JS 引擎,需 UTS 重写) |
总结
@meng-xi/uni-router 为 uni-app 开发者提供了一套完整的路由管理方案:
- vue-router 风格 API:零学习成本,上手即用
- 完整的守卫体系:beforeEach / beforeResolve / afterEach / beforeEnter,支持重定向和超时保护
- 导航动画:三种使用方式,优先级清晰,仅 App 端生效
- uni API 拦截:确保守卫始终生效,无论通过何种方式发起导航
- 页面间通信:EventChannel 双向通信,编程式和声明式均支持
- 路由器就绪超时保护:v1.5.0 新增
readyTimeout,防止 isReady() 永久挂起 - TypeScript 类型提示:路由名称和路径自动补全
- 错误处理体系:完整的错误码和全局错误捕获
- 组合式 API:useRouter / useRoute,响应式路由状态
- 路由状态同步:处理浏览器后退、物理返回键等场景
