需求:根据登录用户的权限,显示不同导航菜单也,同时操作不同的界面。
(1)首先在本地配置好固定不变的路由地址,例如登录,首页这些页面,如下:
import Vue from 'vue'
import Router from 'vue-router'
import LoginView from '@/views/login/Common'
Vue.use(Router)
let constRouter = [
{ path: '/login', name: '登录页', component: LoginView },
{ path: '/index', name: '首页', redirect: '/home' }
]
let router = new Router({
routes: constRouter
})
export default router
(2)全局前置守卫:router.beforeEach,渲染动态路由。
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
-
to: Route
: 即将要进入的目标 路由对象 -
from: Route
: 当前导航正要离开的路由 -
next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。
const whiteList = ['/login']
let asyncRouter
router.beforeEach((to, from, next) => {
if (whiteList.indexOf(to.path) !== -1) {
next()
}
let token = db.get('USER_TOKEN')
let user = db.get('USER')
let userRouter = get('USER_ROUTER') // 获取本地缓存
if (token.length && user) {
if (!asyncRouter) {
if (!userRouter) {
// 请求用户菜单列表接口
} else {
asyncRouter = userRouter
}
} else {
next()
}
} else {
next('/login')
}
})
主要逻辑分为下面几步:
-
判断要跳转的路由地址是否在路由白名单内,是的话放行,不是的话进行第2步;
-
从内存中获取token和用户信息,如果存在则进行第3步,不存在跳转到登录页;
-
判断动态路由信息是否存在,存在则放行,不存在则进行第4步;
-
判断用户路由是否已经加载,是的话将用户路由赋值给动态路由,并执行路由添加操作,然后跳转;如果用户路由不存在,则执行第5步;
-
根据用户名从后台获取用户路由信息,并将其保存到内存中,再执行路由添加操作,然后跳转
(3)通过接口返回的权限菜单列表信息;
request.get(`menu/${user.username}`).then((res) => {
asyncRouter = res.data
save('USER_ROUTER', asyncRouter) // 本地缓存
go(to, next)
})
接口返回的路由信息:
(4)拿到权限路由信息后,需要我们在本地对数据进行处理,注册动态路由:router.addRoutes 是动态添加更多的路由规则。参数必须是一个符合 routes
选项要求的数组。
function go (to, next) {
asyncRouter = filterAsyncRouter(asyncRouter)
router.addRoutes(asyncRouter)
next({...to, replace: true})
}
(5)通过 filterAsyncRouter 方法,将传入的路由数组中 component 属性做处理之后,返回数组。
import Vue from 'vue'
import Router from 'vue-router'
import MenuView from '@/views/common/MenuView'
import PageView from '@/views/common/PageView'
import EmptyPageView from '@/views/common/EmptyPageView'
import HomePageView from '@/views/HomePage'
function filterAsyncRouter (routes) {
return routes.filter((route) => {
let component = route.component
if (component) {
switch (route.component) {
case 'MenuView': route.component = MenuView break
case 'PageView': route.component = PageView break
case 'EmptyPageView': route.component = EmptyPageView break
case 'HomePageView': route.component = HomePageView break
default: route.component = view(component)
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
}
})
}
(6)根据路由,导入对应真实的文件信息:
function view (path) {
return function (resolve) {
import(`@/views/${path}.vue`).then(mod => {
resolve(mod)
})
}
}
(7)如何正确显示左边菜单栏信息呢?
// 底层组件
render (h) {
return h(
Menu,
{
props: {
theme: this.$props.theme,
mode: this.$props.mode,
openKeys: this.openKeys,
selectedKeys: this.selectedKeys
},
on: {
openChange: this.onOpenChange,
select: (obj) => {
this.selectedKeys = obj.selectedKeys
this.$emit('select', obj)
}
}
}, this.renderMenu(h, this.menuData)
)
}
(8)父组件调用菜单组件:
// GlobalLayout.vue
<sider-menu :theme="theme" :menuData="menuData" :collapsed="false" :collapsible="false" @menuSelect="onMenuSelect"/>
let menuData = []
beforeCreate () {
let routers = this.$db.get('USER_ROUTER')
menuData = routers.find((item) => item.path === '/').children.filter((menu) => {
let meta = menu.meta
if (typeof meta.isShow === 'undefined') {
return true
} else return meta.isShow
})
}
// SiderMenu.vue
<i-menu :theme="theme" :collapsed="collapsed" :menuData="menuData" @select="onSelect"/>
转存失败重新上传取消