项目创建过程中经常遇到的一个需求就是权限管理,本文就我在使用动态路由的过程中进行一个总结
首先,前端想要实现对菜单的动态渲染实际上有两种操作方式
1.前端还是书写静态路由表,根据后端传来的code利用v-if去判断渲染菜单项
实际这种使用方式并不可取,因为不方便后期维护。
2.前端只写没有权限控制的login,register, 404 页面的路由,其余路由则是根据后端传过来的,拼接入路由表。
这里我在项目中使用的第二种方法
首先,我们要先理解动态路由表的请求和添加都是发生在路由守卫里边的。
不废话直接上代码
const whiteList = ["/login", "/register"]; // 白名单,不需要token也可以进入的页面
router.beforeEach(async (to, from, next) => {
NProgress.start();
// document.title = to.meta.title;
// 获取用户token,用来判断当前用户是否登录
const hasToken = getToken();
if (hasToken) {
if (whiteList.indexOf(to.path) !== -1) {
next();
NProgress.done();
} else {
// 用来获取后台拿到的路由
const route = await store.getters.addRouters;
// console.log("to.path", to.path, route);
if (route && route.length > 0) {
if (to.path === "/") {
next("/console");
}
next();
NProgress.done();
} else {
store.dispatch("queryMenuList").then(({ servers }) => {
// console.log("拿到的初始路由结构", servers);
filterAsyncRouter(servers);
const menus = store.getters.addRouters;
const routerMap = newRouter(menus);
for (let key in routerMap) {
router.addRoute("layout", routerMap[key]);
}
router.addRoute({ // 这里是对404页面的一个重定向
path: "*",
redirect: "/404",
hidden: true,
});
let homeChildren = router.options.routes[2];
// homeChildren = menus
homeChildren["children"] = routerMap;
// console.log("最终的", menus);
next({
...to,
replace: true,
}); // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
NProgress.done();
});
}
next();
NProgress.done();
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
// 需要跳转的路由是否是whiteList中的路由,若是,则直接条状
next();
} else {
// 需要跳转的路由不是whiteList中的路由,直接跳转到登录页
// console.log("redirect",to.fullPath)
next("/login?redirect=" + to.fullPath);
// 结束精度条
NProgress.done();
}
}
});
store.dispatch(“queryMenuList”)这里是我在vuex中定义的请求动态路由表的方法;
filterAsyncRouter方法
从后端直接请求过来的动态路由表往往是不能直接使用的,我们需要把他的数据结构转换成我们前端路由表的结构。并且把组件对应到路由中去。
const _import = require("@/router/_import_" + process.env.NODE_ENV); //获取组件的方法
function filterAsyncRouter(asyncRouterMap) {
// 过滤动态路由
const newArr = [];
asyncRouterMap.forEach((item) => {
const component = _import(item.component);
// console.log("component", component, item.component ,item.path);
let routerTemplate = {
name: item.serverName,
id: item.id,
path: item.path,
component,
};
if (item.children && item.children.length > 0) {
routerTemplate.children = filterRouter(item.children);
}
newArr.push(routerTemplate);
});
return newArr;
}
_import是引入组件的方法,具体作用就是把组件对应到路由表中去
module.exports = (file) => {
// console.log("传进来的组件地址", file);
var a = true;
try {
// (resolve) => require("@/views/" + file + "/index.vue", resolve)
return require("@/views/" + file + "/index.vue").default;
} catch (e) {
a = false;
}
if (a) {
// console.log("要找的地址2", "@/views/" + file + "/index.vue");
return require("@/views/" + file + "/index.vue").default;
}
};
拿到合格的路由之后我们就可以把路由放到项目路由表中去了
先看一下我们的静态路由
// 配置项目中没有涉及权限的公共路由
export const constantRoutes = [
{
path: "/login", // 登陆
component: login,
},
{
path: "/register", // 登陆
component: register,
},
{
path: "/",
name: "layout",
component: layout,
children: [],
},
{
path: "/404",
name: "notFound",
component: notFound,
},
// {
// path: "*",
// redirect: "/404",
// },
];
我使用的是vue-router是@3.5.2版本所以添加路由使用的是addRoute方法,低版本的vue-router可以尝试addRoutes方法。(ps:addRoute方法需要一个对象一个对象添加,因此下边使用了for循环,addrouters方法可以直接把数组添加到路由表中)
for (let key in routerMap) {
router.addRoute("layout", routerMap[key]);
}
router.addRoute({
path: "*",
redirect: "/404",
hidden: true,
});
或者:addRoute可以选则路由插入位置。
router.addRoutes(menus.concat([{
path: '*',
redirect: '/404',
hidden: true
}]));
给大家解释一下上边对404的疑问,之所以静态路由表把重定向去掉,是因为使用动态路由是异步加载,如果把重定向放静态路由就会导致404优先级变得很高。放在异步路由之后再加载重定向,无疑是最合理且符合项目要求的。
到这里异步路由就加载完毕了,可以正常使用。后边就是对多级菜单的动态渲染了,如果有需求可以参考另一篇文章
对多级菜单的动态渲染