场景:多角色管理系统中,会根据不同的角色划分不同的路由
-
正常的路由
const routes = [ { path: '/', component: () => import('~/layouts/Admin.vue'), children: [ { path: '/', alias: '/home', meta: { title: '后台首页' }, component: () => import('~/pages/Index.vue') }, { path: '/category/list', meta: { title: '分类列表' }, component: () => import('~/pages/category/List.vue') }, { path: '/goods/list', meta: { title: '商品管理' }, component: () => import('~/pages/goods/List.vue') }, ] }, { path: '/login', meta: { title: '登录页' }, component: () => import('~/pages/Login.vue') }, { path: '/:pathMatch(.*)*', component: () => import('~/pages/404.vue') } ] -
假如要动态渲染菜单
- 后端返回的菜单的数据列表如下(可自行和后端沟通)
[
{
name: "后台面板",
icon: "help",
frontpath: null,
child: [
{
name: "主控台",
icon: "home-filled",
frontpath: "/",
}
]
},
{
name: "商品管理",
icon: "shopping-bag",
frontpath: null,
child: [
{
name: "商品管理",
icon: "shopping-cart-full",
frontpath: "/goods/list",
},
{
name: "分类管理",
icon: "menu",
frontpath: "/category/list",
}
]
}
]
- 动态路由开始
router/index.js
// 默认路由,所有用户共享
// 先写出所有角色都通用的路由
const routes = [
{
path: '/',
// 加上name
// router.addRoute('admin', { path: 'settings', component: AdminSettings })
name: 'admin',
component: () => import('~/layouts/Admin.vue'),
},
{
path: '/login',
meta: {
title: '登录页'
},
component: () => import('~/pages/Login.vue')
},
{
path: '/:pathMatch(.*)*',
component: () => import('~/pages/404.vue')
}
]
// 动态路由,用于匹配菜单动态添加路由
// 把所有路由都定义出来,然后根据角色返回的路由列表匹配再动态添加
const asyncRoutes = [
{
path: '/',
name: '/',
meta: {
title: '后台首页'
},
component: () => import('~/pages/Index.vue')
},
{
path: '/category/list',
name: '/category/list',
meta: {
title: '分类列表'
},
component: () => import('~/pages/category/List.vue')
},
{
path: '/goods/list',
name: '/goods/list',
meta: {
title: '商品管理'
},
component: () => import('~/pages/goods/List.vue')
},
]
// 动态添加路由的方法
export function addRoutes(menus) {
// 是否匹配了新的路由
let hasNewRouters = false
const addRoutesByMenus = (arr) => {
arr.forEach(menuItem => {
// 匹配菜单数据和动态路由的路径是否有相同
let item = asyncRoutes.find(obj => obj.path == menuItem.frontpath)
// 如果匹配到了,并且当前路由里没有相同的
// router.hasRoute(name)
// 它的参数是name
if (item && !router.hasRoute(item.path)) {
// 添加路由
router.addRoute("admin", item)
hasNewRouters = true
}
// 如果有子路由,递归
if (menuItem.child && menuItem.child.length > 0) {
addRoutesByMenus(menuItem.child)
}
});
}
addRoutesByMenus(menus)
console.log(router.getRoutes())
return hasNewRouters
}
// 全局路由守卫的时候动态去加载它
// 全局前置守卫
router.beforeEach(async (to, from, next) => {
// 是否已经匹配了新的路由
let hasNewRouters = false
// 如果已登录,自动获取用户信息,并存储到vuex中
if (token) {
// 获取到菜单参数
const { menus } = await store.dispatch('GetInfo')
hasNewRouters = addRoutes(menus)
}
next()
})
此时动态路由已经添加上了,点击菜单的跳转(导航栏输入)已经是可以跳转了
但是,刷新浏览器的时候会发现,找不到当前路由
原因是:router.addRoute()只支持
router.push()或router.replace()来手动导航,才能显示该新路由问题: 1. 刷新找不到路由 2. 每次切换路由都会请求一次菜单并且重新渲染路由
-
解决方法
- 这时hasNnewRouters参数就起作用了
- 定义一个变量防止重复请求
// 是否已经请求了菜单
let hasGetInfo = false
router.beforeEach(async (to, from, next) => {
// 是否已经匹配了新的路由
let hasNewRouters = false
// 如果已登录,自动获取用户信息,并存储到vuex中
if (token && !hasGetInfo) {
// 获取到菜单参数
const { menus } = await store.dispatch('GetInfo')
hasGetInfo = true
hasNewRouters = addRoutes(menus)
}
hasNewRouters ? next(to.fullPath) : next()
})