在权限系统开发中,根据后端返回的菜单列表动态添加路由是非常常见的需求,它可以实现根据用户权限动态加载可访问的页面。在上一篇文章中,我们已经了解到如何渲染侧边栏菜单。本篇文章我们将重点介绍如何优化动态路由的添加过程。
将菜单列表转换为路由格式
学过 vue 的都知道,vue 路由包含 name,path,component 等属性,其中 component 属性是一个函数返回一个模块,比如
{
path: "/",
name: "Layout",
component: () =>
import(/* webpackChunkName: "Layout" */ "../layout/index.vue"),
}
但是后端返回的却是一个字符串,如aa/bb
,所以我们需要一个函数将后端返回的菜单列表处理成路由格式,在 utils 下新建filterRoute.ts
// 匹配views里面所有的.vue文件
const modules = import.meta.glob("../views/**/*.vue");
export const loadView = (view: any) => {
let res;
for (const path in modules) {
const dir = path.split("views/")[1].split(".vue")[0];
if (dir === view) {
res = () => modules[path]();
}
}
return res;
};
export const filterRoute = (data: any) => {
data.forEach((item: any) => {
if (item.children?.length > 0) {
delete item.component;
filterRoute(item.children);
} else {
item.component = loadView(item.component);
// item.redirect = "/404";
}
});
return data;
};
其中,import.meta.glob("../views/**/*.vue")
可以匹配到 views 下所有后缀为.vue
的文件,然后通过对比后端返回的 component 与views/
后面的路径来生成 vue 路由中component
所需要的格式。同时 filterRoute 函数中如果菜单是父菜单,则其不能有 component 属性。
根据以上规则可以看出,如果菜单 component 配置了aa/bb
则我们需要在 views 目录下创建aa/bb.vue
文件才能匹配当前组件路径
引入 Pinia
pinia 是一个很好用的状态管理器,它可以方便的管理全局的状态,首先安装pinia
npm i pinia
然后在main.ts
中注册
import { createPinia } from "pinia";
const app = createApp(App);
const pinia = createPinia();
app.use(pinia).use(router).mount("#app");
新建store/index
文件用于存放 pinia 管理的状态,同时这里我们定义了获取菜单的方法GenerateRoutes
import { defineStore } from "pinia";
import { getMenuList } from "@/http/menu/index";
import router from "@/router";
import { MenuVo } from "@/http/menu/types/menu.vo";
type StoreState = {
isCollapse: boolean,
menuList: MenuVo[],
};
export default defineStore("home", {
state: (): StoreState => {
return {
isCollapse: false,
menuList: [],
};
},
actions: {
async GenerateRoutes() {
const { data } = await getMenuList({});
this.menuList = data;
return data;
},
},
});
动态添加路由
在路由配置文件中router/index.ts
,我们先定义好公共路由页面
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import home from "@/store";
import { nextTick } from "vue";
import { filterRoute } from "@/utils/filterRoute";
const router = createRouter({
history: createWebHashHistory(),
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return { top: 0 };
}
},
routes: [
{
path: "/",
name: "Layout",
component: () =>
import(/* webpackChunkName: "Layout" */ "../layout/index.vue"),
},
{
path: "/:pathMatch(.*)*",
component: () => import("../views/404.vue"),
},
{
path: "/login",
name: "login",
component: () =>
import(/* webpackChunkName: "login" */ "../views/login.vue"),
},
],
});
export default router;
然后在路由守卫beforeEach
中使用addRoute
进行动态路由添加,同时定义一个白名单路由列表,这里暂时只有一个login
页面,表示直接放行
import home from "@/store";
import { nextTick } from "vue";
import { filterRoute } from "@/utils/filterRoute";
...
const writeLists = ['login']
router.beforeEach(async (to, from, next) => {
if (writeLists.includes(to.name)) {
next();
return;
}
await nextTick();
const homeStore = home();
if (homeStore.menuList.length) {
next();
return;
}
const data = await homeStore.GenerateRoutes();
const routers = filterRoute(data);
routers.forEach((route: RouteRecordRaw) => {
router.addRoute("Layout", route);
});
next({ ...to, replace: true });
});
Tips
-
这里在获取 pinia 中数据时,先执行了 await nextTick();
是因为此时如果直接拿 pinia 中数据是拿不到的,因为 pinia 还没有初始化完毕 -
next({ ...to, replace: true })
再次加载当前路由,即重新执行beforeEach
,因为第一次进入 beforeEach 函数中的时候还没有加载动态路由,所以我们需要在添加完路由后重新加载当前路由以触发 beforeEach 钩子函数
路由跳转
我们模拟修改一下菜单表中子菜单 2.1 的 component 为test2/index

相应的我们则需要在 views 下新建test2/index.vue
<template>
<div>test2</div>
</template>
<script lang='ts' setup>
</script>
来到layout/components/sidebar.vue
中处理菜单选择事件

其中 getPath 中d
参数代表当前菜单的父菜单和子菜单 index 的数组,即父菜单的 path 和子菜单 path,如['aa','bb']
,刚好根据它们进行相应的路由跳转
最后点击子菜单2.1
就会发现这个页面被渲染出来了

写在最后
到这里动态路由添加基本完成了,当然可能还会有一些细枝末节需要处理,后面发现的话会进行完善。如果你想系统的了解一个权限管理系统如何搭建的话可以关注公众号查看菜单[实战项目]->[后台权限管理系统]查看完整教程,分为前端篇和后端篇,当然如果你只对前端感兴趣的话,关注前端篇即可
最后动动小手给个赞和在看吧👍
原文始发于微信公众号(web前端进阶):Vue + Element Plus 实现权限管理系统(四):动态添加路由
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/232216.html