0


vue3+vue-router+vite 实现动态路由

文章中出现的代码是演示版本,仅供参考,实际的业务需求会更加复杂

什么是动态路由

在这里插入图片描述

什么场景会用到动态路由

举一个最常见的例子,比如说我们要开发一个后台管理系统,一般来说后台管理系统都会分角色登录,这个时候也就涉及到了权限,比如说这个后台管理系统现在有

超级管理员,管理员,运维,财务等

这几个角色,每个角色登录系统之后都会有不同的权限,超级管理员需要所有的权限,财务可能只需要财务相关的模块(菜单)以及按钮等,通常实现这种需求会有以下常见方案

路由表由前端去维护

也就是说我们已知这几个角色分别对应哪些权限,前端写好路由配置表,,后端只需要告诉你当前登录人是属于哪个角色,前端根据角色类型去写一个过滤函数,获取该角色所拥有的菜单以及按钮,然后动态的去添加路由,这样做有一个缺点就是,如果又增加了一个新的角色怎么办?某个角色想增加一些权限怎么办?这个时候前端就需要去新增或者修改代码,一点也不友好

路由表的数据由后端返回

这种也是常用的一种方式,可能我们的系统里面需要开发一些功能,如

菜单管理

,

角色管理

,

用户管理

等,也就是常说的权限中心,前端开发完的页面所对应的路由信息有后端去维护,这个时候我们在开发的时候需要约束某一种规则,比如所有页面都放到

/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){returntrue;}else{const{ menuList, getMenuList }=useUserStore();// 如果为空数组,name就请求接口重新获取后端维护的路由数据if(!menuList.length){awaitgetMenuList();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

正是我们想要的动态获取的文件路径
以下代码仅供参考,有很多需要完善的地方,只为演示使用

importtype{ RouterType }from"@/router/type";import router from"@/router";importtype{ RouteRecordRaw }from"vue-router";exportconstuseRouterConfig=()=>{// 获取views目录下的所有的文件 不要使用@别名 const modules =import.meta.glob("../views/**");const asyncRoutes =ref<RouterType[]>([]);constaddRoutes=(menus: RouterType[])=>{
    asyncRoutes.value = menus;filterAsyncRouter();// 动态添加 / 路由
    router.addRoute({
      path:"/",
      redirect: asyncRoutes.value[0].path,});};constfilterAsyncRouter=()=>{constrouterLoop=(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 asany,};// 动态添加路由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);};constresolveComponent=(path:string)=>{console.log(modules);// 拿到views下面的所有文件之后,动态拼接`key`去获取valueconst importPage = modules[`../views${path}`];if(!importPage){thrownewError(`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类型,比如分为模块,菜单,页面,按钮等,到时候结合业务去实现逻辑
  • 导航守卫使用时的注意事项,否则会出现刷新白屏,或者路由访问死循环等

本文转载自: https://blog.csdn.net/YuShiYue/article/details/140155531
版权归原作者 YuShiYue 所有, 如有侵权,请联系我们删除。

“vue3+vue-router+vite 实现动态路由”的评论:

还没有评论