基于 vue-antd-admin 开发项目后的几点感想(二):异步路由和菜单

简介: 基于 vue-antd-admin 开发项目后的几点感想(二):异步路由和菜单

前言


上一篇 讲了使用 vue-antd-admin 开发中台项目后的一些感想,并没有提到其动态路由的配置,主要是因为当时觉得其文档写的比较详细了,而且当时并没有遇到什么坑,没想到今天接了一个需求,需要开发一套消息系统,其中有一个显示全部消息的页面,我按照官方文档中的方法配置了页面路由后,发现和我想要的效果不同,其直接全屏显示了,而我需要的是显示在侧边栏菜单的右侧内容部分,于是仔细看了文档和源码后,花了亿点点时间解决了这个需求,并且产出了本篇文章。


路由与菜单


我长话短说,基本的使用一定要看官方文档,概述起来就是说,vue-antd-admin 项目也是完全依赖 vue-router 并使用其配置规则,其提供了同步、异步两种路由方案:

同步的非常简单,就是我们熟悉的vue-router 的配置, 示例代码可以看这个 src/router/config.js就不讲了;

关键是异步,首先需要本地配置路由的map 文件,也就是把所有的完整路由拆分成单个的路由配置进行注册, 文件在这里 /router/async/router.map.js

然后根据接口返回的路由配置与map 文件结合生成最终的路由,接口返回的路由格式如下


[{
  router: 'root',                           //匹配 router.map.js 中注册名 registerName = root 的路由
  children: [                               //root 路由的子路由配置
    {
      router: 'dashboard',                  //匹配 router.map.js 中注册名 registerName = dashboard 的路由
      children: ['workplace', 'analysis'],  //dashboard 路由的子路由配置,依次匹配 registerName 为 workplace 和 analysis 的路由
    },
    {
      router: 'form',                       //匹配 router.map.js 中注册名 registerName = form 的路由
      children: [                           //form 路由的子路由配置
        'basicForm',                        //匹配 router.map.js 中注册名 registerName = basicForm 的路由
        'stepForm',                         //匹配 router.map.js 中注册名 registerName = stepForm 的路由
        {
          router: 'advanceForm',            //匹配 router.map.js 中注册名 registerName = advanceForm 的路由
          path: 'advance'                   //重写 advanceForm 路由的 path 属性
        }
      ]   
    },
    {
      router: 'basicForm',                  //匹配 router.map.js 中注册名 registerName = basicForm 的路由
      name: '验权表单',                     //重写 basicForm 路由的 name 属性
      icon: 'file-excel',                   //重写 basicForm 路由的 icon 属性
      authority: 'form'                     //重写 basicForm 路由的 authority 属性
    }
  ]
}]


重点来了,异步路由是不能覆盖所有的路由的,比如404、login 之类的页面是不需要配置到接口权限里的,所有我们需要一个基础路由配置文件,里面注册的路由都可以merge 到最终的路由配置中。

文件地址在这里 /router/async/config.async.js

官方文档中基础路由的配置使用方法如下:


const routesConfig = [
  'login',                      //匹配 router.map.js 中注册的 registerName = login 的路由
  'root',                       //匹配 router.map.js 中注册的 registerName = root 的路由
  {
    router: 'exp404',           //匹配 router.map.js 中注册的 registerName = exp404 的路由
    path: '*',                  //重写 exp404 路由的 path 属性
    name: '404'                 //重写 exp404 路由的 name 属性
  },
  {
    router: 'exp403',           //匹配 router.map.js 中注册的 registerName = exp403 的路由
    path: '/403',               //重写 exp403 路由的 path 属性
    name: '403'                 //重写 exp403 路由的 name 属性
  }
]


如果按照此方法配置,最终的路由文件大概是这样子


1687778630960.png


可以看到 消息中心是和跟路由平级的,此时的效果是这样的


1687778637899.png


看来我需要把 notification 路由给添加到首页下的children 属性里,其才会在侧边栏生成新的菜单,我修改一下配置方法试试

然后发现,/首页下的children 中并没有这个路由,也就是说其并没有别merge 到一起;问题出在 路由merge 函数里,这个函数在这里 src/utils/routerUtil.jsloadRoutes 中的


// loadRoutes 函数中的这行是关键
const finalRoutes = mergeRoutes(basicOptions.routes, routes)


那么看下它是怎么定义的


/**
 * 合并路由
 * @param target {Route[]} 本地基础路由
 * @param source {Route[]} 接口异步路由
 * @returns {Route[]}
 */
function mergeRoutes(target, source) {
  const routesMap = {}
  target.forEach(item => routesMap[item.path] = item)
  source.forEach(item => {
    routesMap[item.path] = item
  })
  return Object.values(routesMap)
}


可以看到,其是一个浅拷贝函数,同名属性会被覆盖掉,额看来我需要改造一下写一个深度拷贝函数,突然我发现 mergeRoutes 函数下紧接着就有一个定义好的 深度合并路由函数!


/**
 * 深度合并路由
 * @param target {Route[]}
 * @param source {Route[]}
 * @returns {Route[]}
 */
function deepMergeRoutes(target, source) {
  // 映射路由数组
  const mapRoutes = routes => {
    const routesMap = {}
    routes.forEach(item => {
      routesMap[item.path] = {
        ...item,
        children: item.children ? mapRoutes(item.children) : undefined
      }
    })
    return routesMap
  }
  console.log(target, source)
  const tarMap = mapRoutes(target)
  const srcMap = mapRoutes(source)
  // 合并路由
  const merge = _merge(srcMap, tarMap)
  // 转换为 routes 数组
  const parseRoutesMap = routesMap => {
    return Object.values(routesMap).map(item => {
      if (item.children) {
        item.children = parseRoutesMap(item.children)
      } else {
        delete item.children
      }
      return item
    })
  }
  return parseRoutesMap(merge)
}


可以看到其是用深度优先的递归方式来进行merge 操作的; 现在我把 mergeRoutes 替换为 deepMergeRoutes 尝试下发现真的成功了!


1687778620668.png


虽然在官方文档中并没有看到这个函数的身影,但是作者显然考虑到了这种需求,这样,异步路由的方案使用起来就更方便了。

对了,官方还在 routes 的元数据属性 meta 中注入了三个属性 icon、invisible 和 page,其中 invisible 将控制其是否显示在侧边栏菜单中,这个很有用。


总结


今天通过一个具体的需求探究,介绍了 vue-antd-admin 的动态路由方案,本来我还觉得它不太好用,今天无意中发现了 deepMergeRoutes 函数,通过它本地基础路由的配置方便很多,现在看来这个异步动态路由方案还是挺不错的,点赞。

目录
相关文章
|
21小时前
|
JavaScript
【vue】如何跳转路由到指定页面位置
【vue】如何跳转路由到指定页面位置
19 0
|
21小时前
|
JavaScript
vue路由导航守卫(全局守卫、路由独享守卫、组件内守卫)
vue路由导航守卫(全局守卫、路由独享守卫、组件内守卫)
43 0
|
21小时前
vue3配置路由报错Catch all routes (“*“) must now be defined using a param with a custom regexp.
vue3配置路由报错Catch all routes (“*“) must now be defined using a param with a custom regexp.
55 0
|
21小时前
|
资源调度 JavaScript 前端开发
Vue的路由管理:VueRouter的配置和使用
【4月更文挑战第24天】VueRouter是Vue.js的官方路由管理器,用于在单页面应用中管理URL路径与组件的映射。通过安装并引入VueRouter,设置路由规则和创建router实例,可以实现不同路径下显示不同组件。主要组件包括:`<router-link>`用于创建导航链接,`<router-view>`负责渲染当前路由对应的组件。此外,VueRouter还支持编程式导航和各种高级特性,如嵌套路由、路由参数和守卫,以应对复杂路由场景。
|
21小时前
|
JavaScript 数据可视化 算法
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
38 6
|
21小时前
|
JavaScript
vue中watch监听路由传来的参数变化问题
vue中watch监听路由传来的参数变化问题
7 0
|
21小时前
|
资源调度 JavaScript 前端开发
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
|
21小时前
|
JavaScript Go
Vue路由跳转及路由传参
Vue路由跳转及路由传参
|
21小时前
|
JavaScript 前端开发
vue3+ts+element home页面侧边栏+头部组件+路由组件组合页面教程
这是一个Vue.js组件代码示例,展示了带有侧边栏导航和面包屑导航的布局。模板中使用Element Plus组件库,包含可折叠的侧边栏,其中左侧有 Logo 和导航列表,右侧显示更具体的子菜单。`asideDisplay`控制侧边栏宽度。在`script`部分,使用Vue的响应式数据和生命周期钩子初始化路由相关数据,并从localStorage恢复状态。样式部分定义了组件的颜色、尺寸和布局。
19 1
|
21小时前
|
缓存 JavaScript 前端开发
Vue.js 路由时用于提高应用程序性能
Vue.js 路由时用于提高应用程序性能