vue3+vue-router+vite实现动态路由的全过程
作者:YuShiYue
什么是动态路由
什么场景会用到动态路由
举一个最常见的例子,比如说我们要开发一个后台管理系统,一般来说后台管理系统都会分角色登录,这个时候也就涉及到了权限,比如说这个后台管理系统现在有超级管理员,管理员,运维,财务等这几个角色,每个角色登录系统之后都会有不同的权限,超级管理员需要所有的权限,财务可能只需要财务相关的模块(菜单)以及按钮等,通常实现这种需求会有以下常见方案
路由表由前端去维护
也就是说我们已知这几个角色分别对应哪些权限,前端写好路由配置表,,后端只需要告诉你当前登录人是属于哪个角色,前端根据角色类型去写一个过滤函数,获取该角色所拥有的菜单以及按钮,然后动态的去添加路由,这样做有一个缺点就是,如果又增加了一个新的角色怎么办?某个角色想增加一些权限怎么办?这个时候前端就需要去新增或者修改代码,一点也不友好
路由表的数据由后端返回
这种也是常用的一种方式,可能我们的系统里面需要开发一些功能,如菜单管理
,角色管理
,用户管理
等,也就是常说的权限中心,前端开发完的页面所对应的路由信息有后端去维护,这个时候我们在开发的时候需要约束某一种规则,比如所有页面都放到/src/views
目标下面,然后通过菜单管理去维护数据,维护完的数据可以到角色管理里面去配置角色菜单,配置完角色,可以到用户管理里面给某个用户配置角色等一些列流程,这样的话即使新增一个角色,或者修改一个角色的角色菜单,前端都不需要去修改代码,只要在菜单管理里面维护好了数据,想怎么改怎么改,后端返回的数据格式可能如下:
[ { path: "/application", name: "application", component: "Layout", title: "应用管理", show: true, icon: "", children: [ { path: "", name: "application-index", component: "/application/index.vue", }, ], }, { path: "/permission", name: "permission", component: "Layout", title: "权限管理", show: true, icon: "", children: [ { path: "menu", name: "permission-menu", component: "/permission/menu/index.vue", title: "菜单管理", show: true, icon: "", }, { path: "user", name: "permission-user", component: "/permission/user/index.vue", title: "用户管理", show: true, icon: "", }, { path: "role", name: "permission-role", component: "/permission/role/index.vue", title: "角色管理", show: true, icon: "", }, ], }, ]
其实仔细观察这个数据结构,是不是有点熟悉,path
,name
,component
,children
,我们好像手动维护路由表的时候也会用到这些属性
如何实现动态路由
实现动态路由其实就要用到vue-router
提供的一个方法,叫addRoute
在之前版本的时候是addRoutes
,现在非版本的vue-router
给废除了addRoute()
如何使用呢?可以看一下官方文档
当我们添加一个主路由
的时候
router.addRoute({ path: '/permission', name: 'permission', component: () => import('xxxxx')})
添加子路由
也就是嵌套路由
router.addRoute('主路由的name', { path: 'settings', component: AdminSettings })
既然我们已经知道了addRoute()
方法如何使用,下面我们就可以去实现这部分逻辑
我们看一下官方文档的导航守卫里面的内容
在之前我们使用导航守卫的时候需要一个参数next()
去控制是否放行以及去哪个页面,但是在新版本的vue-router
里面可以不是用next()
,当然你是用也行~
我们可以新建一个permission.ts
文件
import router from "./index"; import { useSessionStorage } from "@vueuse/core"; import { StorageEnum } from "@/enum"; import { useUserStore } from "@/store"; const whiteList = ["/login", "/404"]; router.beforeEach(async (to) => { const token = useSessionStorage(StorageEnum.TOKEN, "").value; // 如果在白名单里面 并且 token 不存在 if (whiteList.includes(to.path) && !token) { return true; } else { const { menuList, getMenuList } = useUserStore(); // 如果为空数组,name就请求接口重新获取后端维护的路由数据 if (!menuList.length) { await getMenuList(); console.log("获取全部路由 =>", router.getRoutes()); // 触发重定向 不这样写会导致刷新找不到路由 两种写法都行 // return { path: to.fullPath }; return to.fullPath; } } });
这里我们可以看到一会return true
一会return to.fullpath
是为什么,通过官方导航守卫
里面的介绍,我们可以知道的是
通过官方文档动态路由
,我们可以直到
所以说return to.fullpath
是官方告诉我们要这么使用,也是为了解决动态路由
页面刷新的时候会出现页面空白
或者404
的问题
出现404的话比如说你在路由表中维护了下面路由映射
{ path: "/:pathMatch(.*)", name: "page404", component: () => import("@/views/system/404.vue"), }
上面说的主要是在全局导航守卫
里面的一些使用及注意事项,我们可以看到并没有用到addRoute()
这个方法,也没有出现拿到后端数据前端转换成路由表的相关代码,但是我们可以看到有一个getMenuList()
函数,我们在开发的时候,肯定是要考虑到复用以及复杂逻辑抽取的问题,一个动态路由的实现会牵扯到很多知识点接口请求,vuex或者pinia状态维护,vue-router,vite里面怎么获取文件等
下面看一下如何获取到接口给的数据,转换成vue-router能够识别的数据
首先我们要看一个vite官方给提供的功能,我们拿到后端给的文件路径
如上面代码出现的component
字段,如/application/index.vue
,这个文件可能在我们本地项目中的src/views/application/index.vue
这个路径下
我们需要把它转换成一个下面的格式() => import('xxxx')
,我们需要动态的去拼接获取文件,该怎么实现呢?
vite官方文档中有说明
需要使用import.meta.glob
,这个时候我们可以打印一下import.meta.glob("../views/**")
,看一下返回的到底是什么
返回的是一个对象,而对象的key
我们可以拼接获取到,而value
正是我们想要的动态获取的文件路径
以下代码仅供参考,有很多需要完善的地方,只为演示使用
import type { RouterType } from "@/router/type"; import router from "@/router"; import type { RouteRecordRaw } from "vue-router"; export const useRouterConfig = () => { // 获取views目录下的所有的文件 不要使用@别名 const modules = import.meta.glob("../views/**"); const asyncRoutes = ref<RouterType[]>([]); const addRoutes = (menus: RouterType[]) => { asyncRoutes.value = menus; filterAsyncRouter(); // 动态添加 / 路由 router.addRoute({ path: "/", redirect: asyncRoutes.value[0].path, }); }; const filterAsyncRouter = () => { const routerLoop = (routes: RouterType[], ParentName?: string) => { routes.forEach((item) => { if (item.component === "Layout") { item.component = () => import("@/layout/index.vue"); } else { item.component = resolveComponent(item.component); } const { title, show, icon, name, path, component, children } = item; const route: RouteRecordRaw = { component, path, name, meta: { title, show, icon, }, children: children as any, }; // 动态添加路由 if (ParentName) { router.addRoute(ParentName, route); } else { router.addRoute(route); } if (item.children && item.children.length > 0) { routerLoop(item.children, item.name); } }); }; routerLoop(asyncRoutes.value); }; const resolveComponent = (path: string) => { console.log(modules); // 拿到views下面的所有文件之后,动态拼接`key`去获取value const importPage = modules[`../views${path}`]; if (!importPage) { throw new Error( `Unknown page ${path}. Is it located under Pages with a .vue extension?` ); } return importPage; }; return { addRoutes }; };
综上所述:
要想实现vite+vue-router实现动态路由我们需要用到
addRoute()
import.meta.glob()
获取后端tree数据,递归循环,可能业务会有type类型,比如分为模块,菜单,页面,按钮等,到时候结合业务去实现逻辑
导航守卫使用时的注意事项,否则会出现刷新白屏,或者路由访问死循环等
总结
到此这篇关于vue3+vue-router+vite实现动态路由的文章就介绍到这了,更多相关vue-router+vite动态路由内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!