uni-app路由管理神器:vue-router风格体验

163 阅读5分钟

为 uni-app 提供类似 vue-router 风格的路由管理系统

前言

在 uni-app 开发中,路由管理一直是一个痛点。原生 API(uni.navigateTouni.redirectTouni.navigateBack)虽然能满足基本导航需求,但缺少路由守卫、命名路由、元信息等 vue-router 开发者习以为常的能力。每次需要登录拦截、页面标题设置、权限控制时,都不得不在各个页面中重复编写逻辑。

@meng-xi/uni-router 的目标很简单:让 uni-app 也能拥有 vue-router 级别的路由管理体验,同时不破坏 uni-app 原有的页面模型。

核心特性

vue-router 风格 API

如果你用过 vue-router,上手成本为零:

const router = useRouter()

// 路径导航
await router.push('/pages/about/index')

// 带查询参数
await router.push({ path: '/pages/about/index', query: { id: '1' } })

// 命名路由导航
await router.push({ name: 'about' })

// 替换当前页面
await router.replace('/pages/about/index')

// 返回
await router.back()
await router.back(2) // 返回两级

底层自动映射到 uni-app 原生 API:

uni-router 方法uni-app API
push()uni.navigateTo / uni.switchTab
replace()uni.redirectTo / uni.switchTab
back()uni.navigateBack

路由守卫

这是 uni-router 最核心的能力。四层守卫机制,完整覆盖导航生命周期:

// 全局前置守卫 - 最常用的登录拦截
router.beforeEach((to, from, next) => {
	if (to.meta.requireAuth && !isLoggedIn()) {
		next({ path: '/pages/login/index', query: { redirect: to.fullPath } })
	} else {
		next()
	}
})

// 全局解析守卫 - 所有前置守卫通过后执行
router.beforeResolve((to, from, next) => {
	next()
})

// 全局后置钩子 - 导航完成后执行,无法改变导航结果
router.afterEach((to, from) => {
	console.log(`导航完成: ${from.path}${to.path}`)
})

// 路由独享守卫 - 在路由配置中定义
const routes = [
	{
		path: '/pages/admin/index',
		name: 'admin',
		beforeEnter: (to, from, next) => {
			if (isAdmin()) next()
			else next({ path: '/pages/403/index' })
		}
	}
]

守卫执行顺序:beforeEachbeforeEnterbeforeResolve → 导航 API → afterEach

守卫支持三种行为:

  • next() - 放行
  • next({ path: '/login' }) - 重定向
  • next(false) - 中止导航

命名路由

告别硬编码路径字符串,通过 name 进行导航:

// 路由配置
const routes = [{ path: '/pages/about/index', name: 'about', meta: { title: '关于' } }]

// 导航时使用名称
await router.push({ name: 'about' })
await router.push({ name: 'about', query: { tab: 'info' } })

配合 @meng-xi/vite-plugingenerateRouter 插件,路由名称从 pages.json 自动生成,无需手动维护。

路由元信息

meta 中附加自定义数据,配合守卫实现权限控制、标题设置等:

const routes = [
	{ path: '/pages/index/index', name: 'home', meta: { title: '首页' } },
	{ path: '/pages/user/index', name: 'user', meta: { requireAuth: true } },
	{ path: '/pages/settings/index', name: 'settings', meta: { title: '设置', requireAuth: true } }
]

// 守卫中读取 meta
router.beforeEach((to, from, next) => {
	if (to.meta.requireAuth && !isLoggedIn()) {
		next({ name: 'login' })
	} else {
		// 设置页面标题
		if (to.meta.title) {
			uni.setNavigationBarTitle({ title: to.meta.title })
		}
		next()
	}
})

TypeScript 类型提示

启用 dts: true 后,路由名称和路径获得自动补全和类型检查:

// 路由名称自动补全
router.push({ name: 'pagesAboutIndex' }) // ✅ 自动补全
router.push({ name: 'invalidName' }) // ❌ 类型错误

// 路径自动补全
router.push({ path: '/pages/about/index' }) // ✅ 自动补全
router.push({ path: '/invalid/path' }) // ❌ 类型错误

错误处理

完整的错误体系,支持全局捕获和局部 try/catch:

// 全局错误处理
router.onError((error, to, from) => {
	switch (error.code) {
		case 'NAVIGATION_DUPLICATED':
			console.warn('重复导航', error.message)
			break
		case 'NAVIGATION_ABORTED':
			console.warn('导航被中止')
			break
		case 'NAVIGATION_API_ERROR':
			console.error('导航 API 调用失败', error.cause)
			break
	}
})

// 局部错误处理
try {
	await router.push('/pages/protected/index')
} catch (e) {
	if (e.code === 'NAVIGATION_ABORTED') {
		// 导航被守卫中止
	}
}
错误码说明
NAVIGATION_ABORTED导航被守卫中止
NAVIGATION_CANCELLED导航被取消(重定向超限)
NAVIGATION_DUPLICATED重复导航到当前位置
ROUTE_NOT_FOUND未找到匹配的路由
NAVIGATION_API_ERRORuni 导航 API 调用失败
SETUP_ERROR路由器初始化或使用方式错误

组合式 API

在 Vue 3 组合式 API 中便捷访问路由器:

import { useRouter, useRoute } from '@meng-xi/uni-router'

// 在 setup 中使用
const router = useRouter()
const route = useRoute()

console.log(route.path) // 当前路径
console.log(route.query) // 查询参数
console.log(route.meta) // 元信息

RouterLink 组件

声明式导航,模板中直接使用:

<template>
	<RouterLink to="/pages/about/index">关于页面</RouterLink>
	<RouterLink :to="{ name: 'about' }" replace>替换导航</RouterLink>
	<RouterLink :to="{ path: '/pages/about/index', query: { id: '1' } }"> 带参数导航 </RouterLink>
</template>

<script setup>
import { RouterLink } from '@meng-xi/uni-router/components/RouterLink.vue'
</script>

uni API 拦截

可选功能,启用后直接调用 uni.navigateTo 等原生 API 也会走路由守卫流程:

const router = createRouter({
	routes,
	strict: true,
	interceptUniApi: true // 启用拦截
})

启用后,以下调用都会自动经过守卫链:

// 这两种方式等价,都会触发 beforeEach / afterEach
await router.push('/pages/about/index')
uni.navigateTo({ url: '/pages/about/index' })

路由状态同步

当页面通过浏览器后退、物理返回键等非路由器方式切换时,调用 syncRoute() 确保路由状态与实际页面一致:

// 在页面 onShow 中调用
onShow() {
  router.syncRoute()
}

快速开始

1. 安装

pnpm add @meng-xi/uni-router
pnpm add @meng-xi/vite-plugin -D

2. 配置 Vite 插件

// vite.config.ts
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { generateRouter } from '@meng-xi/vite-plugin'

export default defineConfig({
	plugins: [
		uni(),
		generateRouter({
			pagesJsonPath: 'src/pages.json',
			outputPath: 'src/router.config.ts',
			dts: true,
			metaMapping: {
				navigationBarTitleText: 'title',
				requireAuth: 'requireAuth'
			}
		})
	]
})

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
})

// 注册守卫
router.beforeEach((to, from, next) => {
	if (to.meta.requireAuth && !isLoggedIn()) {
		next({ path: '/pages/login/index', query: { redirect: to.fullPath } })
	} else {
		next()
	}
})

export function createApp() {
	const app = createSSRApp(App)
	app.use(router)
	return { app }
}

4. 在组件中使用

<script setup>
import { useRouter, useRoute } from '@meng-xi/uni-router'

const router = useRouter()
const route = useRoute()

async function goToAbout() {
	await router.push({ name: 'about' })
}
</script>

与 pages.json 的关系

Uni Router 不替代 pages.json,而是与之配合:

职责pages.jsonUni Router
页面注册必须声明不负责
路由导航uni.navigateTo 等push / replace / back
路由守卫不支持beforeEach 等
路由元信息不支持meta 字段
命名路由不支持name 字段

API 概览

Router 实例方法

方法说明
router.push(location)导航到新页面
router.replace(location)替换当前页面
router.back(delta?)返回上一页或多级页面
router.syncRoute()同步路由状态与实际页面栈
router.beforeEach(guard)注册全局前置守卫
router.beforeResolve(guard)注册全局解析守卫
router.afterEach(guard)注册全局后置钩子
router.onError(handler)注册错误处理回调
router.resolve(location)解析路由位置(不导航)
router.getRoutes()获取所有路由配置
router.hasRoute(name)检查路由是否存在

写在最后

v1.0.0 是 @meng-xi/uni-router 的首个正式版本,涵盖了 uni-app 路由管理中最核心的需求:导航、守卫、元信息、类型提示。如果你在 uni-app 项目中需要比原生 API 更强大的路由能力,欢迎试用。

反馈和贡献请前往 GitHub Issues