RuoYi-Vue前端路由:动态路由与菜单生成
在前后端分离的权限管理系统中,动态路由和菜单生成是核心功能之一。RuoYi-Vue作为基于Vue & Element的权限管理系统,其前端路由系统设计实现了根据用户权限动态加载路由和生成菜单的功能。本文将深入解析RuoYi-Vue前端路由的实现机制,包括路由配置、动态路由生成以及菜单渲染的完整流程。
路由系统架构概览
RuoYi-Vue的前端路由系统采用了"基础路由+动态路由"的混合架构。基础路由包含登录、404等无需权限控制的公共页面,动态路由则根据用户角色权限从后端动态获取并生成。
核心实现文件包括:
- 路由配置:ruoyi-ui/src/router/index.js
- 权限控制:ruoyi-ui/src/permission.js
- 路由生成:ruoyi-ui/src/store/modules/permission.js
- 菜单渲染:ruoyi-ui/src/layout/components/Sidebar/index.vue
路由配置结构
路由配置主要分为两部分:公共路由(constantRoutes)和动态路由(dynamicRoutes)。公共路由定义在路由配置文件中,包含系统运行必需的基础页面:
// 公共路由
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error/404'),
hidden: true
},
// 首页等其他公共页面...
]
// 动态路由,基于用户权限动态加载
export const dynamicRoutes = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
}
// 其他需要权限控制的路由...
]
动态路由生成流程
动态路由的生成是RuoYi-Vue路由系统的核心功能,实现了基于用户权限的路由访问控制。整个流程从用户登录成功后开始,主要包括权限获取、路由生成和路由加载三个阶段。
权限路由获取时机
在用户登录成功后,系统会在路由前置守卫中检查用户权限信息是否已加载。如果未加载,则通过GetInfo action获取用户信息,然后调用GenerateRoutes action生成路由:
// 在permission.js的路由前置守卫中
router.beforeEach((to, from, next) => {
if (getToken()) {
if (store.getters.roles.length === 0) {
// 获取用户信息
store.dispatch('GetInfo').then(() => {
// 生成可访问路由
store.dispatch('GenerateRoutes').then(accessRoutes => {
router.addRoutes(accessRoutes) // 动态添加路由
next({ ...to, replace: true })
})
})
}
}
})
动态路由生成过程
动态路由的生成主要在permission store模块的GenerateRoutes action中实现:
- 首先通过API从后端获取路由数据:
// 向后端请求路由数据
getRouters().then(res => {
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
// ...
})
- 然后通过
filterAsyncRouter函数将后端返回的路由数据转换为Vue Router可识别的路由对象:
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else if (route.component === 'ParentView') {
route.component = ParentView
} else {
route.component = loadView(route.component)
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type)
}
return true
})
}
- 路由组件通过
loadView函数实现懒加载,提高系统性能:
export const loadView = (view) => {
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
} else {
// 使用 import 实现生产环境的路由懒加载
return () => import(`@/views/${view}`)
}
}
菜单渲染实现
路由生成后,需要将路由数据转换为菜单组件渲染到页面上。菜单渲染的核心组件是Sidebar,位于ruoyi-ui/src/layout/components/Sidebar/index.vue。
菜单组件结构
Sidebar组件使用Element UI的el-menu和自定义的sidebar-item组件实现菜单渲染:
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
mode="vertical"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
</el-scrollbar>
</div>
</template>
路由与菜单的映射关系
Sidebar组件通过sidebarRouters获取路由数据,该数据由permission store提供:
computed: {
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route
const { meta, path } = route
// 如果设置了meta.activeMenu,则高亮指定路径
if (meta.activeMenu) {
return meta.activeMenu
}
return path
}
}
sidebar-item组件会递归处理路由数据,生成多级菜单结构。对于有子路由的菜单项,会自动生成下拉菜单,实现了菜单的层级展示。
动态路由权限控制
RuoYi-Vue的动态路由系统不仅实现了路由的动态加载,还结合了权限控制机制,确保用户只能访问其权限范围内的路由和菜单。
权限过滤机制
在动态路由生成过程中,会通过filterDynamicRoutes函数根据用户权限过滤路由:
// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
const res = []
routes.forEach(route => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route)
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route)
}
}
})
return res
}
路由元信息配置
路由配置中的meta字段用于定义路由的元信息,包括菜单标题、图标、是否缓存等:
{
path: 'index',
component: () => import('@/views/index'),
name: 'Index',
meta: {
title: '首页',
icon: 'dashboard',
affix: true
}
}
其中:
title:菜单显示的标题icon:菜单图标,对应ruoyi-ui/src/assets/icons/svg目录下的SVG图标文件affix:是否固定在tags-view中
菜单图标使用
系统使用SVG图标作为菜单图标,图标文件存储在ruoyi-ui/src/assets/icons/svg目录下。使用时只需在路由的meta.icon中指定图标文件名即可:
实际应用场景
动态路由和菜单生成功能在实际应用中发挥着重要作用,以下是几个典型场景:
多角色权限控制
不同角色的用户登录系统后,会看到不同的菜单。例如,管理员可以看到"系统管理"菜单,而普通用户则无法看到。
菜单动态显示/隐藏
通过路由配置中的hidden属性,可以控制菜单是否在侧边栏显示:
{
path: '/system/user-auth',
component: Layout,
hidden: true, // 不在侧边栏显示
permissions: ['system:user:edit'],
children: [
// ...
]
}
动态面包屑导航
基于路由信息自动生成的面包屑导航,帮助用户了解当前所在位置:
// Breadcrumb组件
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="index === levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
<router-link v-else :to="item.redirect || item.path">{{ item.meta.title }}</router-link>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
总结与扩展
RuoYi-Vue的动态路由与菜单生成系统通过"基础路由+动态路由"的架构,结合权限控制机制,实现了灵活而安全的路由管理。核心优势包括:
- 按需加载:只加载用户权限范围内的路由和组件,提高性能
- 灵活配置:通过路由元信息控制菜单显示、面包屑导航等
- 权限集成:与后端权限系统无缝集成,确保访问安全
对于开发者而言,可以通过扩展路由元信息实现更多自定义功能,例如:
- 添加菜单排序字段控制显示顺序
- 增加菜单徽章显示通知数量
- 实现菜单的个性化定制
掌握RuoYi-Vue的动态路由机制,不仅有助于更好地使用该框架进行开发,也能为构建其他复杂权限系统提供参考。
官方文档:doc/若依环境使用手册.docx 路由配置:ruoyi-ui/src/router/index.js 权限控制:ruoyi-ui/src/permission.js
希望本文能帮助你深入理解RuoYi-Vue的前端路由系统,如果觉得有帮助,请点赞收藏并关注后续更多技术解析!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



