动态路由设置一般有两种:
**一、简单的角色路由设置(前端控制)**
这种方式适用于角色较少且权限相对固定的场景。前端预先定义好所有可能的路由和对应的角色权限,然后根据用户登录时获取的角色信息来筛选出用户可访问的路由。
**代码实现:**
1、**路由配置文件 (router/index.js)**:定义静态路由和异步路由(权限路由)。
javascript
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/layout'
import store from '@/store' // 假设使用 Vuex 存储用户角色
Vue.use(VueRouter)
// 静态路由,无需权限即可访问
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
}
]
// 异步路由,根据角色权限加载
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
redirect: '/permission/page',
alwaysShow: true,
name: 'Permission',
meta: {
title: '权限管理',
roles: ['admin', 'editor'] // 普通的用户角色
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: '页面权限',
roles: ['editor'] // editor角色的用户才能访问该页面
}
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: '角色权限',
roles: ['admin'] // admin角色的用户才能访问该页面
}
}
]
},
{
path: '/dashboard',
component: Layout,
name: 'Dashboard',
meta: {
title: '仪表盘',
roles: ['admin', 'user'] // admin 和 user 都可以访问
}
}
// 可以继续添加更多异步路由...
]
const createRouter = () => new VueRouter({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// 用于重置路由的方法(例如用户登出后)
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重置路由匹配器
}
export default router
2、**权限过滤工具 (utils/asyncRouter.js)**:根据用户角色过滤异步路由。
javascript
/**
* 判断当前用户是否有访问该路由的权限
* @param {Array} roles - 用户的角色列表
* @param {Object} route - 路由对象
* @returns {Boolean}
*/
export function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
// 如果路由定义了需要的角色,则检查用户角色是否包含在内
return roles.some(role => route.meta.roles.includes(role))
} else {
// 如果没有定义角色,则默认允许访问
return true
}
}
/**
* 递归过滤异步路由表,筛选出用户有权限访问的路由
* @param {Array} routes - 异步路由列表
* @param {Array} roles - 用户的角色列表
* @returns {Array}
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
// 如果有子路由,递归过滤子路由
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
3、**路由守卫 (permission.js)**:在路由跳转前进行权限判断。
javascript
import router from './router'
import store from './store'
import { getToken } from '@/utils/auth' // 假设有一个获取 token 的工具函数
import { filterAsyncRoutes } from '@/utils/asyncRouter' // 引入权限过滤工具
import NProgress from 'nprogress' // 进度条
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login'] // 白名单,不需要登录即可访问的路由
router.beforeEach(async (to, from, next) => {
// 开始进度条
NProgress.start()
// 获取 token
const token = getToken()
if (token) {
// 如果已经登录
if (to.path === '/login') {
// 如果访问的是登录页,则跳转到首页
next({ path: '/' })
NProgress.done()
} else {
// 检查用户信息是否已获取
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
// 如果用户角色已存在,则直接进入
next()
} else {
try {
// 获取用户信息(通常包含角色)
const { roles } = await store.dispatch('user/getUserInfo')
// 根据用户角色过滤路由
const accessRoutes = filterAsyncRoutes(asyncRoutes, roles)
// 动态添加路由
router.addRoutes(accessRoutes)
// 将过滤后的路由存入 store(可选,用于菜单渲染等)
store.commit('permission/SET_ROUTES', accessRoutes)
// 继续导航
next({ ...to, replace: true })
} catch (error) {
// 获取用户信息失败,清除 token 并跳转到登录页
await store.dispatch('user/resetToken')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
// 如果未登录
if (whiteList.indexOf(to.path) !== -1) {
// 如果是白名单中的路由,则直接进入
next()
} else {
// 否则跳转到登录页
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
// 路由结束
router.afterEach(() => {
NProgress.done()
})
**二、复杂的路由权限设置(后端控制)**
这种方式适用于权限复杂、角色多变的系统。前端不预先定义所有路由,而是由后端返回用户当前拥有的路由列表,前端根据这个列表动态添加路由。
**代码实现:**
1、**路由配置文件 (router/index.js)**:只定义静态路由和一个空的动态路由数组
javascript
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/layout'
import store from '@/store'
Vue.use(VueRouter)
// 静态路由,无需权限即可访问
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
}
]
// 动态路由,由后端返回
export const asyncRoutes = []
const createRouter = () => new VueRouter({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
2、**API 请求工具 (api/user.js)**:用于获取用户权限路由列表。
javascript
import request from '@/utils/request'
// 获取用户信息(包含角色)
export function getUserInfo() {
return request({
url: '/user/info',
method: 'get'
})
}
// 获取用户权限路由列表(后端返回)
export function getPermissionRoutes() {
return request({
url: '/user/permission/routes',
method: 'get'
})
}
**3、路由守卫 (permission.js)**:在用户登录后,调用后端接口获取路由列表并动态添加。
javascript
import router from './router'
import store from './store'
import { getToken } from '@/utils/auth'
import { getPermissionRoutes } from '@/api/user' // 引入获取权限路由的 API
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login']
router.beforeEach(async (to, from, next) => {
NProgress.start()
const token = getToken()
if (token) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// 获取用户信息(通常包含角色)
const { roles } = await store.dispatch('user/getUserInfo')
// 获取后端返回的权限路由列表
const routeData = await getPermissionRoutes()
// 假设后端返回的路由数据结构是 { routes: [...], ... }
const accessRoutes = routeData.routes || []
// 动态添加路由
router.addRoutes(accessRoutes)
// 将过滤后的路由存入 store(可选,用于菜单渲染等)
store.commit('permission/SET_ROUTES', accessRoutes)
next({ ...to, replace: true })
} catch (error) {
await store.dispatch('user/resetToken')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})
总结说明:
一 (前端控制):通过在路由配置中定义 meta.roles 字段来标记路由所需的角色。在路由守卫中,根据用户角色调用 filterAsyncRoutes 函数过滤出用户可访问的路由,并使用 router.addRoutes 动态添加。这种方式简单直接,适合权限结构相对固定的场景。
二 (后端控制):前端不预先定义路由,而是通过调用后端 API 获取用户权限对应的路由列表。后端根据用户角色和权限规则生成路由配置(通常包含 path, component, meta 等信息),前端接收到后直接使用 router.addRoutes 添加。这种方式更灵活,适合权限复杂、需要频繁调整的系统,但需要后端配合提供路由数据接口。
两种方式的核心都是利用 router.addRoutes (Vue Router 3.x) 或 router.addRoute (Vue Router 4.x) 来动态添加路由,并结合路由守卫进行权限判断
注意:vue是单页面应用程序,所以页面一刷新数据部分数据也会跟着丢失,所以我们需要将store中的数据存储到本地,才能保证路由不丢失