前端权限之动态路由树

前言

在工作过程中,我们经常会遇到动态加载路由树的需求,本篇文章主要写动态加载路由树的实现方式。

路由定义

项目中肯定有一些页面是每个用户都能访问的(如:登陆、注册),这些路由我们可以直接做定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//routers.js
export default [
{
path: "/login",
name: "login",
meta: {
title: "登录",
hideInMenu: true
},
component: () => import("@/view/login/login.vue")
},
{
path: "/401",
name: "error_401",
meta: {
hideInMenu: true
},
component: () => import("@/view/error-page/401.vue")
},
{
path: "/500",
name: "error_500",
meta: {
hideInMenu: true
},
component: () => import("@/view/error-page/500.vue")
}
// {
// path: '*',
// name: 'error_404',
// meta: {
// hideInMenu: true
// },
// component: () => import('@/view/error-page/404.vue')
// }
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//router/index.js
import Router from "vue-router";
import routes from "./routers";
// process.env.NODE_ENV 根据不同的环境 加载不同的路由模板处理文件
const _import = require("./config/_import_" + process.env.NODE_ENV);

Vue.use(Router);
// console.log('process.env.WEBPACK_ENV', process.env.WEBPACK_ENV, process.env.BASE_URL);

const router = new Router({
base: process.env.BASE_URL, //此处是做项目基础路径的,可忽略
routes: routes,
mode: "history"
});

动态路由树

1
2
3
4
5
6
7
8
9
10
11
//login.vue
import { mapActions } from "vuex";
export default {
methods: {
...mapActions(["handleLogin", "initRoutes"]),
handleSubmit(res) {
//后台返回路由树
this.initRoutes([this, res.resourceMenuDTO]);
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//store/app.js
import { addRoutes } from "@/libs/util";
export default {
state: {
local: localRead("local"),
routers: GET_SESSION_STORAGE("_c_unparseRoutes") || []
},
getters: {
routers: state => {
if (state.routers && state.routers.length > 0) {
return state.routers;
} else {
return JSON.parse(localStorage.getItem("_c_unparseRoutes")) || [];
}
},
homeRoute: (state, getters) => getHomeRoute(getters.routers, homeName)
},
mutations: {
setHomeRoute(state, routes) {
state.homeRoute = getHomeRoute(routes, homeName);
},
SET_ROUTER_CHANGE: (state, params) => {
SET_SESSION_STORAGE("router", params);
state.router = params;
},
setRoutes(state, data) {
state.routers = data;
}
},
actions: {
initRoutes({ commit, rootState }, info) {
let router = addRoutes(info[0], info[1]);
commit("setRoutes", router);
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//libs/util.js

import routes from "@/config/routes";

/**
* @description 根据后台数据返回动态生成路由树
* @param {Object} vm Vue实例
* @param {Object} routeItem 路由对象
*/
export const addRoutes = (vm, routeObj) => {
let routers = [];
btnPre = [];

Object.values(routeObj).forEach((elems, index) => {
if (elems.uri === "/") {
routes["home"].path = process.env.BASE_URL;
routers.push(routes["home"]);
}
let routeList = formatRouter(elems);

routers = routers.concat(routeList);
});

routers.push(routes["404"]); // 坑:如不在此时添加404 刷新页面会自动跳转404

localStorage.setItem("_c_unparseRoutes", JSON.stringify(routers));

return routers;
};

export const formatRouter = (elems, name) => {
let routeList = [];
elems.childList.forEach((elems2, index) => {
let route = getCurPage(elems2.uri);
if (elems2.childList && elems2.childList.length > 0) {
let child = formatRouter(elems2, elems2.uri);
route.children = child;
}

if (route) {
routeList.push(route);
}
});
return routeList;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//config/routes.js
export default {
home: {
path: "/",
redirect: "/AuthManage/UserManage",
component: "main",
meta: {
hideInMenu: true,
icon: "_quanxian",

title: "权限管理",
notCache: true
}
},
AuthManage: {
path: "/AuthManage",
name: "AuthManage",
redirect: "/AuthManage/UserManage",
component: "main",
meta: {
hideInMenu: false,
icon: "_quanxian",
title: "权限管理",
notCache: true
}
},
404: {
path: "*",
name: "error_404",
meta: {
hideInMenu: true
},
component: "view/error-page/404"
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//router/index.js

//路由拦截
var getRouter;

function routerGo(to, next) {
// 过滤路由 调用filterAsyncRouter 数据模板处理方法 返回Vue-router可以识别数据
getRouter = filterAsyncRouter(getRouter);
// 动态添加所有的的路由

router.addRoutes(getRouter);
store.commit('SET_ROUTER_CHANGE', routes.concat(getRouter));
// 调用next()放行
next({ ...to, replace: true });
}

function filterAsyncRouter(asyncRouterMap) {
// 遍历后台传来的路由字符串,转换为组件对象 递归方法

const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
// 路由有component
if (route.component === 'main') {
// 判断路由是Layout 布局组件,将上方引用的Layout布局组件放进去
route.component = Main;
} else {
// 路由不是Layout组件 二级也main
if (route.component === 'parentView') {
route.component = parentView;
} else {
route.component = _import(route.component);
}
}
}
// 判断当前的路由对象中是否含有children 有再次调用本方法 递归调用 直到没有
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children);
}
return true;
});

return accessedRouters;
}

//注:此处为多加的beforeEach中间件,不要跟自己写的重合在一起。

router.beforeEach((to, from, next) => {
iView.LoadingBar.start();
let routers = store.getters.routers;
if (routers && routers.length > 0) {
if (!getRouter) {
// 判断有没有路由权限 没有 路由权限 重新请求|从Vuex中获取
getRouter = routers; // 拿到路由
routerGo(to, next); // 调用动态添加路由的方法
} else {
// 判断有没有路由列表 有 允许进入下个这里是第二个beforeEach
next();
}
} else {
// 判断 数据仓库中有没有 用户登录返回的路由列表 没有 ===>去登陆页
setCookie('oscsToken', '');
next({
name: LOGIN_PAGE_NAME // 跳转到登录页
});
})