vue根据后端菜单数据生成动态路由

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

前言:在之前的项目中,菜单是动态获取的,而路由是写死的,配置路由的时候只要保证路由的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页面,然后再跳转回原先的页面,刷新的速度变慢了,视觉上也会看见一闪的白屏,寻求更好的方法~

 
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页