背景
最近在写一个springboot+vue的rbac后台管理系统,其中权限校验那是必不可少的要素;前端也就自然需要通过vue的addroutes来动态添加用户拥有的路由信息;
思路
再参考了非常多的资料后,开始整理了开发的思路
1 可以把静态路由和动态路由分开添加
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//匹配路由名字获得对应组件
export const ruleMapping = {
'menu': () => import("@/views/system/Menu"),
'user': () => import("@/views/system/User"),
'job': () => import("@/views/system/Job"),
'role': () => import("@/views/system/Role"),
'dept': () => import("@/views/system/Dept"),
}
//已存静态路由
const routes = [
{
path: '/',
redirect: "/home"
},
{
path: '/login',
component: () => import("@/components/Login"),
}
]
//动态路由的头部
export const authRoutes = {
path: '/home',
component: () => import("@/components/Home"),
children: [{
path: '/',
redirect: "/index",
},{
path: '/index',
component: () => import("@/components/Index"),
}]
}
//创建路由的方法
const createRouter = () => new VueRouter({
mode: 'hash',
routes:routes
})
//重置路由信息
export const resetRouter = () => {
const newRouter = createRouter()
router.matcher = newRouter.matcher
};
const router = createRouter()
export default router
2 将后端返回的权限信息封装成vue的路由信息
putAuthList(context, menuVoList) {
let tmpAuthRoutes = deepCopy(authRoutes)
function iteration(menuVoList) {
menuVoList.forEach((menuItem) => {
if (menuItem.routerUrl && menuItem.componentUrl) {
let param = {};
param.path = menuItem.routerUrl
param.component = ruleMapping[menuItem.componentUrl]
tmpAuthRoutes.children.push(param)
}
if (menuItem.childMenu) {
iteration(menuItem.childMenu)
}
})
}
iteration(menuVoList);
router.addRoutes([tmpAuthRoutes])
}
3 退出登录后,重置路由
//重置路由信息
export const resetRouter = () => {
const newRouter = createRouter()
router.matcher = newRouter.matcher
};
问题
如上第二步的putAuthList方法(已经是修改后的)就是出问题的集中区域;
尝试1
路由信息是这样的,没有包含所谓的动态头部路由
//已存静态路由,
export let routes = [
{
path: '/',
redirect: "/home"
},
{
path: '/login',
component: () => import("@/components/Login"),
}
,{
path: '/home',
component: () => import("@/components/Home"),
children: [{
path: '/',
redirect: "/index",
},{
path: '/index',
component: () => import("@/components/Index"),
}]
}
]
然后直接把动态路由信息添加到静态路由的children中
putAuthList(context, menuVoList) {
> let tmpAuthRoutes = router.options.routes;
function iteration(menuVoList) {
menuVoList.forEach((menuItem) => {
if (menuItem.routerUrl && menuItem.componentUrl) {
let param = {};
param.path = menuItem.routerUrl
param.component = ruleMapping[menuItem.componentUrl]
> tmpAuthRoutes[2].children.push(param)
if (menuItem.childMenu) {
iteration(menuItem.childMenu)
}
})
}
iteration(menuVoList);
router.addRoutes(tmpAuthRoutes)
}
这样做确实能够进入登录后页面
但是退出后再登录会发现添加了非常多的重复路由信息,虽然使用了resetRouter,但是新的router.routes已经是添加过动态路由的routes了
尝试2
保留头部动态路由的方案,通过深拷贝来复制头部动态路由,头部动态路由副本去添加动态路由,最后再addroutes到路由表中,这样就不会影响到静态路由了;resetRouter也才能生效
//已存静态路由
const routes = [
{
path: '/',
redirect: "/home"
},
{
path: '/login',
component: () => import("@/components/Login"),
}
]
//动态路由的头部
export const authRoutes = {
path: '/home',
component: () => import("@/components/Home"),
children: [{
path: '/',
redirect: "/index",
},{
path: '/index',
component: () => import("@/components/Index"),
}]
}
putAuthList(context, menuVoList) {
> let tmpAuthRoutes = JSON.parse(JSON.stringify(authRoutes))
> console.log(tmpAuthRoutes)
> console.log(authRoutes)
function iteration(menuVoList) {
menuVoList.forEach((menuItem) => {
if (menuItem.routerUrl && menuItem.componentUrl) {
let param = {};
param.path = menuItem.routerUrl
param.component = ruleMapping[menuItem.componentUrl]
> tmpAuthRoutes.children.push(param)
}
if (menuItem.childMenu) {
iteration(menuItem.childMenu)
}
})
}
iteration(menuVoList);
router.addRoutes(tmpAuthRoutes)
}
结果是,根本连首页都进不去,为什么呢?看控制台可以知道,component部分完全没有;是因为JSON.parse(JSON.stringIf()),只是对字符串进行了转换,而获取组件是通过function来完成的;(失败)
尝试3
也就是最开始贴出思路的代码,尝试2和3的区别就是深拷贝方案的不同,这里贴一下深拷贝的函数吧,来源于网络:
/**
* 深拷贝一个值
* @param value
*/
export default function deepCopy(data, hash = new WeakMap()) {
if(typeof data !== 'object' || data === null){
throw new TypeError('传入参数不是对象')
}
// 判断传入的待拷贝对象的引用是否存在于hash中
if(hash.has(data)) {
return hash.get(data)
}
let newData = {};
const dataKeys = Object.keys(data);
dataKeys.forEach(value => {
const currentDataValue = data[value];
// 基本数据类型的值和函数直接赋值拷贝
if (typeof currentDataValue !== "object" || currentDataValue === null) {
newData[value] = currentDataValue;
} else if (Array.isArray(currentDataValue)) {
// 实现数组的深拷贝
newData[value] = [...currentDataValue];
} else if (currentDataValue instanceof Set) {
// 实现set数据的深拷贝
newData[value] = new Set([...currentDataValue]);
} else if (currentDataValue instanceof Map) {
// 实现map数据的深拷贝
newData[value] = new Map([...currentDataValue]);
} else {
// 将这个待拷贝对象的引用存于hash中
hash.set(data,data)
// 普通对象则递归赋值
newData[value] = deepCopy(currentDataValue, hash);
}
});
return newData;
}