Vue Router高级用法与复杂路由场景实现
Vue Router是Vue生态中处理路由的核心工具,除了基础的页面跳转,它还提供了丰富的高级特性,支持复杂应用的路由设计。本文将深入解析Vue Router的高级用法,包括动态路由、嵌套路由、路由守卫等,并结合实际场景说明如何落地实现。
一、动态路由与路由参数:处理变化的路径
动态路由用于匹配路径结构相同但参数不同的路由(如详情页),通过:
语法定义参数,使路由规则具备灵活性。
1. 动态路由配置
// router/index.js(Vue3)
import {createRouter, createWebHistory} from 'vue-router'
import UserProfile from '../views/UserProfile.vue'
const routes = [
// 动态参数:userId,匹配/user/1、/user/2等路径
{path: '/user/:userId', component: UserProfile}
]
const router = createRouter({
history: createWebHistory(),
routes
})
参数规则扩展:
- 多参数:
/post/:postId/comment/:commentId
(匹配/post/1/comment/5
) - 可选参数:
/search/:query?
(query
可省略,匹配/search
和/search/vue
) - 正则约束:
/user/:userId(\\d+)
(仅匹配数字ID,如/user/123
,不匹配/user/abc
)
2. 路由参数的获取与响应
(1)获取参数
选项式API:通过
this.$route.params
获取vue<script> export default { mounted() { console.log('用户ID:', this.$route.params.userId) } } </script>
组合式API:通过
useRoute
钩子获取vue<script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log('用户ID:', route.params.userId) </script>
(2)响应参数变化
当路由参数变化(如从/user/1
跳转到/user/2
)时,组件不会重新创建,需手动监听参数变化:
<!-- 选项式API:监听$route变化 -->
<script>
export default {
watch: {
$route(to, from) {
// 当userId变化时重新加载数据
this.loadUser(to.params.userId)
}
}
}
</script>
<!-- 组合式API:监听route.params -->
<script setup>
import {useRoute, watch} from 'vue-router'
const route = useRoute()
watch(
() => route.params.userId,
(newId) => {
loadUser(newId) // 重新加载数据
}
)
</script>
更优雅的方式:使用组件内路由守卫beforeRouteUpdate
<script>
export default {
beforeRouteUpdate(to, from) {
// 参数变化时触发,可直接修改组件状态
this.userId = to.params.userId
this.loadUser(this.userId)
}
}
</script>
二、嵌套路由:构建层级化页面结构
嵌套路由用于实现“页面内嵌套子页面”的场景(如后台管理系统的侧边栏+主内容区),通过children
配置子路由,配合<router-view>
嵌套渲染。
1. 嵌套路由配置
// router/index.js
const routes = [
{
path: '/dashboard',
component: DashboardLayout, // 父组件(包含公共布局)
children: [
// 子路由:path为空表示默认子路由
{path: '', component: DashboardHome},
// 子路由:/dashboard/settings
{path: 'settings', component: DashboardSettings},
// 子路由:/dashboard/profile
{path: 'profile', component: DashboardProfile}
]
}
]
2. 组件中的嵌套渲染
父组件(DashboardLayout.vue
)需包含<router-view>
用于渲染子路由:
<!-- DashboardLayout.vue -->
<template>
<div class="dashboard">
<!-- 公共侧边栏 -->
<aside>
<nav>
<router-link to="/dashboard">首页</router-link>
<router-link to="/dashboard/settings">设置</router-link>
</nav>
</aside>
<!-- 子路由内容将渲染到这里 -->
<main>
<router-view></router-view>
</main>
</div>
</template>
注意事项:
- 子路由的
path
如果以/
开头,则表示根路径,不受父路由path
影响; - 嵌套层级不宜过深(建议不超过3层),否则会增加维护成本。
三、路由守卫:控制路由访问与跳转
路由守卫用于在路由跳转过程中添加“拦截逻辑”,如登录验证、权限检查、未保存提示等。Vue Router提供三类守卫,覆盖路由生命周期的不同阶段。
1. 全局守卫:影响所有路由
全局守卫注册在路由实例上,对所有路由生效,常用场景:登录验证、全局日志记录。
(1)beforeEach
:路由跳转前触发
// router/index.js
router.beforeEach((to, from, next) => {
// to:目标路由对象
// from:当前导航正要离开的路由
// next:函数,决定是否继续跳转(必须调用)
// 示例:未登录用户访问需要登录的页面时,重定向到登录页
const isLogin = localStorage.getItem('token')
const requiresAuth = to.meta.requiresAuth // 结合元信息判断是否需要登录
if (requiresAuth && !isLogin) {
next('/login') // 重定向到登录页
} else {
next() // 允许跳转
}
})
(2)beforeResolve
:导航确认前触发(所有组件内守卫和异步路由组件解析后)
router.beforeResolve((to, from, next) => {
// 常用于导航确认前的最后检查(如数据预加载)
next()
})
(3)afterEach
:路由跳转后触发(无next
)
router.afterEach((to, from) => {
// 常用于页面埋点、修改页面标题
document.title = to.meta.title || '默认标题'
})
2. 路由独享守卫:仅影响当前路由
在路由配置中通过beforeEnter
定义,仅对当前路由生效,适合单独路由的权限控制。
const routes = [
{
path: '/admin',
component: AdminPanel,
// 路由独享守卫
beforeEnter: (to, from, next) => {
// 示例:仅管理员可访问
const userRole = localStorage.getItem('role')
if (userRole === 'admin') {
next()
} else {
next('/forbidden') // 无权限,跳转到禁止页
}
}
}
]
3. 组件内守卫:控制组件的路由行为
在组件内部定义,用于组件相关的路由逻辑(如未保存表单的离开确认)。
<script>
export default {
// 进入组件前触发(此时组件实例未创建,不能用this)
beforeRouteEnter(to, from, next) {
// 可通过next的回调访问组件实例
next(vm => {
vm.fetchData(to.params.id) // 调用组件方法
})
},
// 组件复用(参数变化)时触发(如从/post/1到/post/2)
beforeRouteUpdate(to, from, next) {
this.id = to.params.id
this.fetchData(this.id)
next()
},
// 离开组件时触发
beforeRouteLeave(to, from, next) {
// 示例:未保存表单时提示确认
if (this.formChanged) {
if (confirm('表单未保存,确定离开吗?')) {
next() // 确认离开
} else {
next(false) // 取消离开
}
} else {
next()
}
}
}
</script>
四、路由懒加载与代码分割:优化首屏加载
路由懒加载通过“按需加载”路由组件,减少首屏JS体积,提升加载速度。其原理是将组件打包为单独的chunk,仅在访问对应路由时才加载。
1. 基础实现(动态import)
// 非懒加载(初始加载时全部加载)
import Home from '../views/Home.vue'
// 懒加载(访问时才加载)
const About = () => import('../views/About.vue')
const routes = [
{path: '/', component: Home},
{path: '/about', component: About}
]
2. 命名chunk(方便调试)
通过/* webpackChunkName: "chunk-name" */
注释指定chunk名称(Webpack/Vite支持):
// 命名为"user"的chunk
const UserProfile = () => import(/* webpackChunkName: "user" */ '../views/UserProfile.vue')
const UserSettings = () => import(/* webpackChunkName: "user" */ '../views/UserSettings.vue')
打包后,这两个组件会被合并到user.js
中,减少HTTP请求。
3. 配合Suspense(Vue3)
Vue3的Suspense
可优雅处理懒加载组件的加载状态:
<template>
<Suspense>
<!-- 加载成功时显示的内容 -->
<template #default>
<router-view></router-view>
</template>
<!-- 加载中显示的占位内容 -->
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
五、路由元信息与滚动行为:精细化控制
1. 路由元信息(meta)
meta
用于存储路由的附加信息(如页面标题、是否需要登录),可在路由守卫中访问,实现灵活的逻辑控制。
// 路由配置
const routes = [
{
path: '/',
component: Home,
meta: {
title: '首页', // 页面标题
requiresAuth: false, // 不需要登录
keepAlive: true // 需要缓存组件
}
},
{
path: '/profile',
component: Profile,
meta: {
title: '个人中心',
requiresAuth: true, // 需要登录
roles: ['user', 'admin'] // 允许访问的角色
}
}
]
// 在全局守卫中使用meta
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = to.meta.title || '应用名称'
// 权限检查
if (to.meta.requiresAuth) {
// 检查登录状态...
}
next()
})
2. 滚动行为(scrollBehavior)
控制路由跳转时页面的滚动位置,提升用户体验(如返回顶部、保持滚动位置)。
const router = createRouter({
history: createWebHistory(),
routes,
// 滚动行为配置
scrollBehavior(to, from, savedPosition) {
// 场景1:按下浏览器后退/前进按钮时,恢复之前的滚动位置
if (savedPosition) {
return savedPosition
}
// 场景2:跳转到新路由时,默认滚动到顶部
return {top: 0}
// 场景3:滚动到指定元素(通过选择器)
if (to.hash) {
return {
el: to.hash, // 滚动到#anchor元素
behavior: 'smooth' // 平滑滚动
}
}
}
})
六、复杂场景实战:动态路由与权限控制
在后台管理系统中,常需要根据用户角色动态生成路由。结合addRoute
方法和权限判断,可实现这一需求。
1. 动态添加路由(addRoute
)
// 路由权限逻辑(store中)
export const usePermissionStore = defineStore('permission', {
actions: {
generateRoutes(roles) {
// 1. 定义所有需要权限的路由
const asyncRoutes = [
{path: '/admin', component: Admin, meta: {roles: ['admin']}},
{path: '/editor', component: Editor, meta: {roles: ['admin', 'editor']}}
]
// 2. 根据用户角色筛选路由
const accessibleRoutes = asyncRoutes.filter(route => {
return route.meta.roles.some(role => roles.includes(role))
})
// 3. 动态添加路由
accessibleRoutes.forEach(route => {
router.addRoute(route) // 添加到根路由
// 若需添加到嵌套路由:router.addRoute('父路由name', route)
})
return accessibleRoutes
}
}
})
2. 实现步骤
- 用户登录后获取角色信息;
- 调用
generateRoutes
根据角色筛选并添加路由; - 路由添加完成后跳转到首页或之前的目标页。
<!-- 登录组件 -->
<script setup>
import {useRouter} from 'vue-router'
import {usePermissionStore} from '../store/permission'
const router = useRouter()
const permissionStore = usePermissionStore()
const handleLogin = async () => {
// 1. 登录获取用户信息(含角色)
const {roles} = await api.login(/* 账号密码 */)
// 2. 动态生成路由
const accessibleRoutes = await permissionStore.generateRoutes(roles)
// 3. 跳转到首页或之前的目标页(如登录前想访问的页面)
router.push(router.history.pending.fullPath || '/')
}
</script>
总结
Vue Router的高级特性为复杂路由场景提供了完整解决方案:
- 动态路由处理参数化路径,适合详情页等场景;
- 嵌套路由构建层级页面,如后台管理系统的布局;
- 路由守卫控制访问权限,实现登录验证、权限检查;
- 懒加载优化首屏性能,减少初始加载时间;
- 元信息与滚动行为提升用户体验,实现精细化控制。
在实际开发中,需根据业务场景合理组合这些特性,同时注意路由设计的简洁性(避免过度嵌套)和可维护性(路由模块化)。掌握这些高级用法,能让你轻松应对从简单应用到大型复杂系统的路由需求。