动态路由
后台管理系统,大部分都会涉及到权限控制这一项需求,即:根据不同登录角色渲染不同页面功能
现在主流有两种方式:
前端控制
逻辑简单,上手快
后端控制
相对安全,需要后期改动
后端控制
后端路由是大部分后台管理项目的解决方案
核心:用户登录以后,后端根据该角色生成可访问的路由数据,前端根据这个路由数据转换成自己需要的路由结构
具体代码结构:
router 文件中,只放一些静态路由和公共路由
代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js
在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52import { constantRoutes } from '@/router'
import { getRoutes } from '@/api/menu'
import Layout from '@/layout/index'
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRoutes(asyncRouterMap) {
return asyncRouterMap.filter(route => {
if (route.component) {
// Layout组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else {
route.component = loadView(route.component)
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRoutes(route.children)
}
return true
})
}
function loadView(view) {
// 注意:webpack4动态import不支持变量方式,如下写法是不行的
// return () => import(`@/views/${view}`)
return resolve => require([`@/views/${view}`], resolve)
}
const permission = {
state: {
routes: [],
addRoutes: [],
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
},
},
actions: {
GenerateRoutes({ commit }) {
const Id = sessionStorage.getItem('SESSION_KEY')
return new Promise(resolve => {
getRoutes(Id).then(res => {
const accessedRoutes = filterAsyncRoutes(res.data)
accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
})
},
},
}给后端整理一份前端需要的 router 数据结构
一般必须有的参数:id、path、name、title、children
1
2
3
4
5
6
7
8
9path: # 路由地址
name: # 路由名称
id: # id
component: # 组件路径
meta->title: # 菜单名称(和path同级就可以)
meta->icon: # 菜单图标(和path同级就可以)
meta->type: # 菜单类型,用于区分模块、目录、菜单、按钮
meta->hidden: # 是否全局隐藏此菜单
children: # 子集集合如果后端传的不是 children,是 parentId 那种类型,则需要写一个转换方法
方法可以参考这篇文章:Vue 封装无限层级树形菜单组件(后台传的是扁平数组)
在导航守卫中,使用
router.beforeEach
进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
router.beforeEach((to, from, next) => {
NProgress.start()
// 判断是否有token
if (store.getters.token) {
if (to.path === '/login') {
next({ path: '/' })
} else {
// 判断当前用户是否已拉取完user_info信息
if (store.getters.roles.length === 0) {
// 获取info信息
store.dispatch('GetInfo').then(res => {
const roles = res.roles
store
.dispatch('GenerateRoutes', { roles })
.then(accessRoutes => {
// 动态添加可访问路由表
router.addRoutes(accessRoutes)
// hack方法 确保addRoutes已完成
next({ ...to, replace: true })
})
.catch(err => {
console.log(err)
})
})
} else {
// 当有用户权限的时候,说明可访问路由表已生成
next()
}
}
} else {
if (whiteList.includes(to.path)) {
// 在免登录白名单,直接进入
next()
} else {
// 否则全部重定向到登录页
next('/login')
}
}
})从 router 中取出可用的路由对象,来进行侧边栏的渲染
也可以参考这篇文章:后端控制路由
前端控制
可以参考花裤衩的文章,手摸手,带你用vue撸后台 系列二(登录权限篇)
核心:通过 token 获取用户的 role,根据 role 动态跟路由表 meta.role
进行匹配,形成可访问的路由再通过 router.addRotes
动态挂载路由
具体代码结构:
- 可以参考:动态路由前端控制还是后端控制?(附代码)
把动态路由和静态路由分别写在 router 文件中(
asyncRoutes/constantRoutes
)。在动态路由的 meta 元信息中添加 roles 权限代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js
在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏
代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/store/modules/permission.js
在导航守卫中,使用
router.beforeEach
进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)https://github.com/PanJiaChen/vue-element-admin/blob/master/src/permission.js
从 router 中取出可用的路由对象,来进行侧边栏的渲染
https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/index.vue