乐趣区

Vue动态菜单(路由)的实现方案(beforeEach+addRoutes+elementUI)

前端路漫漫,挽起袖子干
前言
我之前总结过动态菜单的实现方案 > 动态菜单实现, 只不过这篇写的有点稍微复杂,是用后端返回当前登录角色的路由表实现的,也就是前端只要从后端取到路由表进行渲染菜单即可;
今天,我再讲解一种方案:路由表写在前端,后端返回用户的角色,前端进行角色对应的菜单渲染
在线预览:动态路由 github(记的 star 哈):https://github.com/Mrblackant…
开始之前,自己要大概懂写关于 vue-router 的 beforeEach(路由拦截)、addRoutes,elementUI 的菜单组件等方法,不然理解可能会有点吃力
思路
分以下几步:1. 前端在本地写好路由表,以及每个路由对应的角色,也就是哪些角色可以看到这个菜单 / 路由;2. 登录的时候,向后端请求得到登录用户的角色 (管理者、普通用户);3. 利用路由拦截,根据取到的用户角色, 跟本地的路由表进行对比,过滤出用户对应的路由,并利用路由进行左侧菜单渲染
实现
根据上述的 3 步,我们进行每一步的实现
1. 前端本地写好路由表我们分成两个路由表,一个是固定的,比如首页展示,每个人都能看到,一个需要根据用户角色动态展示的; 这里就利用到了 router 的 meta 属性,我们在这里边写上菜单对应的:icon,对应的哪些角色可以看到这个菜单:roles 一个完整的路由表如下:
// 代码位置:router/index.js
{
path: ”,
component: layout, // 整体页面的布局 (包含左侧菜单跟主内容区域)
children: [{
path: ‘main’,
component: main,
meta: {
title: ‘ 首页 ’, // 菜单名称
roles: [‘user’, ‘admin’], // 当前菜单哪些角色可以看到
icon: ‘el-icon-info’ // 菜单左侧的 icon 图标
}
}]
}
2. 用户登录,取到用户的角色本来我是写了 mock 数据,模拟用户登录,请求后端角色的接口,奈何 mock 挂了,所以我就直接模拟了:
取到用户角色,存放进 localStorage, 然后跳转主页
// 代码位置:src/components/reLoad.vue

// axios.post(‘/temp’,this.formModel).then(res=>{})
// 我暂时就不模拟了,直接取
let getUserRole = this.formModel.user === ‘admin’ ? ‘admin’ : ‘user’
localStorage.setItem(‘userRole’, getUserRole)
this.$router.push({
path: ‘/main’
})
3. 路由拦截 beforeEach, 并过滤出角色对应的路由表
经过第 2 步,我们已经得到了用户的角色,这时候在路由拦截的地方我们就可以取到了,
取到之后,结合第 1 步我们写好的路由,利用数组的 filter 方法,拿角色跟路由表里 meta 标签里的 roless 数据进行对比
过滤好了,拿当前路由去渲染左侧菜单,这一步其实可以用 vuex 去实现,我担心有的小伙伴不理解,就用一个 global(全局变量) 替代了
尤其要注意路由拦截这里,很容易陷入死循环,所以我建议大家先了解一下 beforeEach 和 addRoutes 的运行机制
// 代码位置:src/permission.js

router.beforeEach((to, from, next) => {
// 取到用户的角色
let GetRole = localStorage.getItem(“userRole”)

// 如果登录了
if (GetRole !== ‘unload’) {
next() //next() 方法后的代码也会执行
// 1. 如果路由表 没根据角色进行筛选, 就筛选一次
if (!addRouFlag) {
addRouFlag = true
// 2. 根据用户的角色、和需要动态展示的路由,生成符合用户角色的路由
var getRoutes = baseRoleGetRouters(permissionRouter, GetRole.split(“,”))
// 3. 利用 global 属性,让渲染菜单的组件 sideMeuns.vue 重新生成左侧菜单
global.antRouter = fixedRouter.concat(getRoutes)
// 4. 将生成好的路由 addRoutes
router.addRoutes(fixedRouter.concat(getRoutes))
// 5.push 之后,会重新进入到 beforeEach 的钩子里, 直接进入第一个 if 判断
router.push({path: to.path})
}
} else {
// 用户没登录,跳转到登录页面
if (to.path === ‘/’) {
next()
} else {
next(‘/’)
}
}

})
整体流程走完了,再容易让人蒙的地方
1. 根据路由进行菜单展示代码位置:/src/components/sideMeuns.vue, 先看下 elementUI 菜单组件, 把一些基础的参数先了解一下,这里我把菜单渲染写成了一个组件:用到了递归属性,保证可以生成多级菜单,我建议不熟悉的,大家用组件先模拟着写一个包含跳转功能、icon 展示的菜单,然后再看我写的组件
2. 用户退出系统代码位置:/src/components/layout.vue 退出的时候,记得清除掉存在 localStorage 的用户角色,然后利用 window.location.href = “/” 跳转到登录页,
为什么要用 location.href,这样会把之前 addRoutes 的路由清除掉,确保下个用户登陆后,会重新渲染正确的菜单
如果有些地方不理解,师兄建议把不理解的点先单独拿出来跑跑,或者看看这篇文章的思路来源:手把手 …
如有不正确的地方,还望小伙伴指正哈

退出移动版