当前位置 博文首页 > wusq的博客:vue根据后端菜单数据生成动态路由

    wusq的博客:vue根据后端菜单数据生成动态路由

    作者:[db:作者] 时间:2021-07-16 13:08

    动态路由初体验,存在不足,欢迎点评指正~

    前言:在之前的项目中,菜单是动态获取的,而路由是写死的,配置路由的时候只要保证路由的path与菜单的index(elementUI的el-menu组件)相同就可以实现路由跳转,只是菜单改了的话,路由也得相应修改,否则就会找不到页面。当时之所以没有研究动态路由是觉得跳转的页面是路由指定的,如果路由变成动态获取的话,前端的页面文件命名和目录结构都得根据后端的数据调整,也不是很灵活,但是…最近后端调整了数据,发现好多路由没有匹配到,都跳转到了一个重定向/redirect页面,所以最终还是决定动手研究动态路由

    1.将router/index.js写死的路由注释掉(或者删除)

    2. 新建asyncRouter.js用来根据菜单提取路由表,菜单结构如图:

    name -> componentName

    path -> url

    component -> 由上级菜单的componentName加上本级菜单的componentName组成文件路径

    meta的title -> menuName

    具体代码:

    /*
     * @Author: Wushiqi
     * @Descripttion: 获取路由:遍历传入的菜单列表,拿取拼接路由所需的数据(path、name、title、component)
     * @Date: 2020-08-19 10:44:15
     * @LastEditor: Wushiqi
     * @LastEditTime: 2020-08-25 16:07:33
     */
    // Layout组件是项目中的主页面,切换路由时,仅切换Layout中的组件
    import Layout from '@/layout'
    export function getAsyncRoutes(routes) {
      
      const res = []
      routes.forEach(route => { // 所有菜单都是二级结构,一级没有页面功能,所以只要添加二级菜单的路由
        if (route.childMenuInfoTreeSet.length !== 0) {
          const children = []
          route.childMenuInfoTreeSet.forEach(menu => { // 二级菜单需匹配页面
            children.push({
              path: menu.url,
              name: menu.componentName.split('/')[1],
              // 此处使用require,由于import会有奇怪的错误
              component: (resolve) => require([`@/views${route.componentName + menu.componentName}`], resolve),
              meta: { title: menu.menuName }
            })
          })
          res.push({
            path: route.url,
            component: Layout,
            children: children
          })
        }
      })
      return res
    }
    

    3.在获取菜单的时候同时生成路由表:

    逻辑:首次登录时获取菜单,同时执行getAsyncRoutes方法获取路由表,将得到的路由数据使用addRoutes添加到路由表,并且将每一项路由push到路由的options。注意一个坑:f5刷新页面时,会执行路由的跳转的操作,而再次获取菜单和路由这个操作是在路由跳转之后的,所以在刷新的时候就找不到起始的路由(除了写死的路由之外),解决办法是让再次获取菜单和路由的操作在路由跳转之前,我这边的思路是在第一次获取菜单时,将菜单存到sessionStorage,之后刷新的时候不再访问接口重新获取。

    这是写死的路由(未添加动态数据):

    这是添加了动态数据之后的路由:?

    获取菜单部分代码:

    async created() {
        if (!sessionStorage.menu) { // 首次登陆获取菜单列表
          const response = await publicAPI.userMenuAPI() // 从后台获取菜单列表
          const data = response.data.data.menuList
          if (data.length > 0) {
            this.menuList = data
            const router = getAsyncRoutes(data) // 根据菜单提取路由
            this.$router.addRoutes(router)
            router.forEach(val => { // 将菜单提取出来的路由加到路由表
              this.$router.options.routes.push(val)
            })
            console.log(this.$router);
            sessionStorage.setItem('menu', JSON.stringify(data)) // 将菜单列表存到sessionStorage
          }
        } else { // 已有菜单不再重新获取
          this.menuList = JSON.parse(sessionStorage.menu)
          // 要重新将路由挂上去,否则点击菜单不会跳转页面
          const router = getAsyncRoutes(this.menuList)
          this.$router.addRoutes(router)
          router.forEach(val => { // 将菜单提取出来的路由加到路由表
              this.$router.options.routes.push(val)
           })
        }
      }

    ?4.修改目录和文件名

    根据下面拼接的结果调整目录

    比如用户管理拼接完成是@/views/baseData/user,则目录是这个样子的?

    5.重定向

    我的默认路由有一项是重定向,所有匹配不到的路由都会跳转到重定向页面。

    改为动态路由之后发现,刷新的时候路由会跳转到重定向页面,所以要在重定向页面让路由跳转回原来的页面。

    {
        path: '*',
        redirect: '/redirect/:path*'
      },
      {
        path: '/redirect',
        name: 'redirect',
        component: Layout,
        hidden: true,
        children: [
          {
            path: '/redirect/:path*',
            component: () => import('@/views/redirect/redirect')
          }
        ]
      },

    ??重定向页面代码:

    <script>
    export default {
      created() { // 重定向页面
        const { params } = this.$route
        const { path } = params
        if (path) {
          this.$router.push({ path: `/${path}` })
        } else {
          this.$router.push({ path: `${sessionStorage.toPath}` })
        }
      },
      render: function(h) {
        return h() // avoid warning message
      }
    }
    </script>

    重定向页面的sessionStorage.toPage存的是刷新前访问的页面,相关代码添加在router.beforeEach里面

    if (!to.redirectedFrom && to.path !== '/redirect') { // 如果不是重定向操作,就存下当前访问的路由地址
        sessionStorage.setItem('toPath', to.path)
      }

    这样就完成动态路由啦~ 只是这样以后,刷新页面会先跳转至redirect页面,然后再跳转回原先的页面,刷新的速度变慢了,视觉上也会看见一闪的白屏,寻求更好的方法~

    cs