这段时间一直在搭建移动端APP基础信息管理的PC端后台管理系统,主要功能含 人员基础信息维护 / 人员组织维护 / 人员权限管理 / APP应用数据导入导出 等。由于项目刚上线,正好抽出半天空闲时间(主要还是因为今天刚好是周末)将开发中遇到的一些问题作个简要的归纳和总结。
正如题目说述,这篇文章主要记录在使用vue+element UI 开发后台管理系统时在动态路由和路由权限处理时遇到的问题和踩过的坑。
为防止歧义,这里先声明一下本文所指的动态路由和静态路由的概念:
- 动态路由:指根据用户角色从后台动态获取的路由(和用户的权限和角色相关)
- 静态路由:指公共开放的路由,也就是所有人都能访问的页面(与权限角色无关)这部分路由由前端处理无需从后台获取
权限分为页面级权限和按钮级权限两种,其中页面级权限通过控制路由展示与否的方式控制,按钮级权限通过用户的身份角色控制。
路由权限控制主要分两种思路:
1.首先需要将后端返回的路由数据转换为路由表:
[
{
funcode: null
funid: "JN001001"
funlevel: null
funname: "组织架构"
funpath: "ORGStructure"
funtype: 0
funtype_: null
funurl: "views/SYSManage/ORGStructure.vue"
icourl: "el-icon-c-scale-to-original"
isshow: 1
isshow_: null
parentid: "JN001"
systemid: "JN"
}
]
-此时前端需要根据该路由数据生成路由表。主要是获取component
function generateRouter(authTree){
let asyncRouterMap = authTree.map((list)=>{
return {
path:`/${
list.funpath}`,
name:list.funpath,
component:Layout,
redirect:`/${
list.funpath}`,
hidden:list.isshow==1,
meta:{
title:list.funname,icon:list.icourl},
children:list.children.map((child)=>{
return {
path:`/${
child.funpath}`,
name:child.funpath,
meta:{
title:child.funname,icon:child.icourl},
component:()=>import(`@/${
child.funurl}`) //重要
}
})
}
})
return asyncRouterMap; //返回动态路由表
}
转换函数需要根据后台返回的数据进行调整,实际开发时需要前后端一起约定数据格式,此处仅作参考。
let asyncRouter = null; //储存动态路由,也是判断动态路由是否建立的重要标志
router.beforeEach((to, from, next) => {
if(to.path=="/login"){
asyncRouter = null; //跳转到登录页面时对asyncRouter 初始化
next();
}
/* 首先判断token是否存在 */
else if(!localStorage.getItem("Token")){
Message({
showClose: true,
type: 'error',
message:"请先登录!"
});
next("/login")
}
/*判断动态路由是否已经建立*/
else if(!asyncRouter){
/*如果找不到路由信息,提示用户重新登录 */
if(!localStorage.getItem("router")){
Message({
showClose: true,
type: 'error',
message:"登录信息已过期,请重新登录!"
});
next("/login")
}
/*合并路由 */
else{
/* 调用前面定义的路由生成函数 */
asyncRouter =generateRouter(JSON.parse(localStorage.getItem('router')));
store.commit("setRouter",asyncRouter);
/** 解决路由重复问题 */
router.matcher = createRouter().matcher;
router.addRoutes(asyncRouter);
/**
* 注意:一定要把404页面放到路由的最后面,否则页面刷新时会优先被重定向到404页面
*/
let router404 = [{
path:"*",
redirect: '/404'
}];
router.addRoutes(router404);
next({
...to, replace: true });
}
}
/*一切正常,直接跳转*/
else{
next();
}
})
路由信息在用户登录获取后,同时存储到store和localStorage中,避免页面刷新导致路由丢失
建立变量let asyncRouter = null;
保存动态路由信息和判断路由信息是否存在/*判断动态路由是否已经建立*/else if(!asyncRouter)
否则将会导致全局导航循环执行,最终浏览器堆栈溢出。这个也是自己亲自踩过的坑,想详细了解的可以搜索router.beforeEach() 动态加载路由出现死循环
在这方面踩坑的人不再少数。
404页面问题:如注释中所述。404页面需要放到所有页面之后。否则在任何一个页面刷新都会跳转到404页面。因为404页面优先被处理了。当然如果你使用的是history
模式还应该参考vue官方教程,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。vue-router history 模式配置说明
在路由每次导航至login页面时记得重置asyncRouter。这个也是自己在实际开发中踩过的坑。现在有这样一种场景,用户A登录后退出登录(但未关闭网页),此时页面导航至登录页面使用用户B的账号登录,你会惊奇的发现B居然渲染的是A的路由表信息。
这是因为用户A在推出登录时asyncRouter
仍保留着A的动态路由表,此时B登录进来时会跳过生成过程直接将A的路由表合并成B的路由信息。因此在每次导航到login页面时需要重置asyncRouter以保证加载正确的路由信息。
if(to.path=="/login"){
asyncRouter = null; //跳转到登录页面时对asyncRouter 初始化
next();
}
顺便说一下上述判断可能会导致另一个错误提醒(不是致命的)此时可在main.js中(整个路由导航都应该在main.js中执行)加入如下语句即可修复。
const routerPush = Router.prototype.push
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error=> error)
}
路由重复主要是因为
addRoutes
只负责添加路由,不负责过滤已经存在的路由
当用户A推出登录(未关闭网页)此时用户B接着登录。如果用户A和用户B有相同的路由信息,那么控制台就会出现警告路由重复添加
【此前重置变量asyncRouter只是后端返回的路由信息,而真实生成的路由信息并每页重置,因此会出现路由重复问题】,路由重复问题可通过下述代码解决,通过router.matcher
实现路由的初始化
/** 解决路由重复添加问题 */
router.matcher = createRouter().matcher;
router.addRoutes(asyncRouter);
/*---------------------------------------------*/
/*createRouter 函数*/
export const createRouter = () => new VueRouter({
routes:initRouterMap, //由前端管理的静态路由
base: process.env.BASE_URL,
})
(完)