用户登录
用户注册

分享至

vue后台管理之动态加载路由的方法

  • 作者: 此人无民
  • 来源: 51数据库
  • 2021-11-22

在这里我们将会实现一个vue动态路由的案列,当用户登陆成功后,根据用户的角色,拿到他对应的菜单信息,并将它动态的载入到我们的路由中。

我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。

一、思路

在vue-router对象中首先初始化公共路由,比如(404,login)等,然后在用户登陆成功,根据用户的角色信息,获取对应权限菜单信息menulist,并将后台返回的menulist转换成我们需要的router数据结构,然后通过vue-router2.2新添的router.addrouter(routes)方法,同时我们可以将转后的路由信息保存于vuex,这样我们可以在我们的sidebar组件中获取我们的全部路由信息,并且渲染我们的左侧菜单栏,让动态路由实现。

二、实现

1、公共路由定义

import vue from 'vue'
import router from 'vue-router'

vue.use(router)
/* layout */
import layout from '../views/layout/layout'

export const constantroutermap = [
 { path: '/login', component: () => import('@/views/login/index'), hidden: true },
 { path: '/404', component: () => import('@/views/404'), hidden: true },

 {
  path: '/',
  component: layout,
  redirect: '/dashboard',
  name: 'dashboard',
  hidden: true,
  children: [{
   path: 'dashboard',
   component: () => import('@/views/dashboard/index')
  }]
 },
]
export default new router({
 scrollbehavior: () => ({ y: 0 }),
 routes: constantroutermap
})

2、获取菜单信息

router.beforeeach((to, from, next) => {
 nprogress.start() // start progress bar
 if (gettoken()) { // determine if there has token
  /* has token*/
  if (to.path === '/login') {
   next({ path: '/' })
   nprogress.done() // if current page is dashboard will not trigger aftereach hook, so manually handle it
  } else {
   if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
    store.dispatch('getinfo').then(res => { // 拉取user_info
     const roles = res.roles
     store.dispatch("getmenu").then(data => {
      initmenu(router, data);
     });
     next()
    }).catch((err) => {
     store.dispatch('fedlogout').then(() => {
      message.error(err || 'verification failed, please login again')
      next({ path: '/' })
     })
    })
   } else {
    next()
   }
  }
 } else {
  /* has no token*/
  if (whitelist.indexof(to.path) !== -1) { // 在免登录白名单,直接进入
   next()
  } else {
   next('/login') // 否则全部重定向到登录页
   nprogress.done() // if current page is login will not trigger aftereach hook, so manually handle it
  }
 }
})

router.aftereach(() => {
 nprogress.done() // finish progress bar
})

在这里 我们通过在router的beforeeach钩子函数 判断用户是否已经获得角色信息,如果没有,则请求后台获取对应的角色信息,然后通过角色信息再次请求获取对应的菜单列表,获取到菜单列表,然后去格式化菜单列表,使其转换成router数组的结构。

3、动态加载路由

import store from '../store'

export const initmenu = (router, menu) => {
 if (menu.length === 0) {
  return
 }
 let menus = formatroutes(menu);
 // 最后添加
 let unfound = { path: '*', redirect: '/404', hidden: true }
 menus.push(unfound)
 router.addroutes(menus)
 store.commit('add_routers',menus)
}

export const formatroutes = (amenu) => {
 const arouter = []
 amenu.foreach(omenu => {
  const {
   path,
   component,
   name,
   icon,
   childrens
  } = omenu
  if (!validatenull(component)) {
   let filepath;
   const orouter = {
    path: path,
    component(resolve) {
     let componentpath = ''
     if (component === 'layout') {
      require(['../views/layout/layout'], resolve)
      return
     } else {
      componentpath = component
     }
     require([`../${componentpath}.vue`], resolve)
    },
    name: name,
    icon: icon,
    children: validatenull(childrens) ? [] : formatroutes(childrens)
   }
   arouter.push(orouter)
  }

 })
 return arouter
}

在这里我们把menlist转换成routerlist因为我们后台返回的数据不是规范的router结构,所以这里需要我们处理一下,如果你们后台返回规范的就不需要处理,然后通过router.addroutes把后台返回的加入到我们的路由中,并且将其保存在我们的vuex中,需要主要的 如果404组件一定要放在动态路由在后载入。

4、渲染菜单

其实这里已经不属于我们的所讲的重点,在这里只需要取出上一步存在vuex中的路由信息,并且将其渲染成我们的左侧菜单栏就可以。在这里我们使用了element-ui。

<template>
 <el-scrollbar wrapclass="scrollbar-wrapper">
  <el-menu
   mode="vertical"
   :show-timeout="200"
   :default-active="$route.path"
   :collapse="iscollapse"
   background-color="#304156"
   text-color="#bfcbd9"
   active-text-color="#409eff"
  >
   <sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"></sidebar-item>
  </el-menu>
 </el-scrollbar>
</template>

<script>
import { mapgetters } from 'vuex'
import sidebaritem from './sidebaritem'
import { validatenull } from "@/utils/validate";
import { initmenu } from "@/utils/util";

export default {
 components: { sidebaritem },
 created() {
 },
 computed: {
  ...mapgetters([
   'permission_routers',
   'sidebar',
   'addrouters'
  ]),
  iscollapse() {
   return !this.sidebar.opened
  }
 }
}
</script>

就这样我们动态加载路由就是实现了,是不是很简单,关键点就是router.addroute方法。下面我就说一下防踩坑点。

三、防坑

1、关于加载菜单信息的时机

在此之前我将第二步获取菜单信息放在我的sidebar组件的create函数中,当时我发现也没有什么问题。登录跳转到home界面 左侧菜单也成功渲染,点击菜单进入我们动态加载的路由界面,也没问题。但是当我点击刷新的时候问题来。页面空白 控制台也不报错。当时我就蒙蔽了,什么情况,不是好好的嘛?如果大家也遇到这种这时候大家不要着急,冷静的分析整个流程,就会发现问题的所在。

1、登陆成功跳转home界面,home组件是公共路由,存在的没问题。

2、这时候 sidebar组件create钩子触发,成功获取菜单列表

3、菜单列表转成路由数组,并且加载到router实例中和vuex中

4、sidebar从vuex获取到路由数组渲染菜单 进入我们动态加载页面中,显示正常,这一切看起来没什么问题

5、点击浏览器的刷新按钮、或者f5,页面空白。

原因: 第五步中我们我们浏览器刷新,在spa应用整个vue实例会重新加载,也是说我的vue-router会重新初始化,那么我们之前的动态addroute就不存在了,但是我们此时访问一个不存在的页面,所以我们的sidebar组件也就不会被访问,那么也无法获取菜单信息,就导致页面空白。所以我们需要把加载菜单信息这一步放在router的全局守卫beforeeach中就可以了。

2、关于404组件的位置

大家可以看到

export const initmenu = (router, menu) => {
 if (menu.length === 0) {
  return
 }
 let menus = formatroutes(menu);
 // 最后添加
 let unfound = { path: '*', redirect: '/404', hidden: true }
 menus.push(unfound)
 router.addroutes(menus)
 store.commit('add_routers',menus)
}

我强调了 404组件一定要放在动态路由组件的最后,不然你刷新动态加载的页面,会跳转到404页面的。

四、效果图

动态路由

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

软件
前端设计
程序设计
Java相关