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


Posted in Javascript onAugust 13, 2018

在这里我们将会实现一个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页面的。

四、效果图

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

动态路由

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

Javascript 相关文章推荐
jQuery 操作下拉列表框实现代码
Feb 22 Javascript
JavaScript为对象原型prototype添加属性的两种方式
Aug 01 Javascript
推荐40个非常优秀的jQuery插件和教程【系列三】
Nov 09 Javascript
jQuery数组处理代码详解(含实例演示)
Feb 03 Javascript
完美兼容各大浏览器的jQuery插件实现图片切换特效
Dec 12 Javascript
基于JavaScript实现单选框下拉菜单添加文件效果
Jun 26 Javascript
JCrop+ajaxUpload 图像切割上传的实例代码
Jul 20 Javascript
Javascript6中字符串的四个新用法分享
Sep 11 Javascript
javascript-解决mongoose数据查询的异步操作
Dec 22 Javascript
layui获取选中行数据的实例讲解
Aug 19 Javascript
微信小程序开发实现的IP地址查询功能示例
Mar 28 Javascript
vue实现移动端div拖动效果
Mar 03 Vue.js
jQuery中$原理实例分析
Aug 13 #jQuery
Angular服务Request异步请求的实例讲解
Aug 13 #Javascript
微信小程序中使用wxss加载图片并实现动画效果
Aug 13 #Javascript
深入浅析angular和vue还有jquery的区别
Aug 13 #jQuery
Angular异步变同步处理方法
Aug 13 #Javascript
利用JS实现一个同Excel表现的智能填充算法
Aug 13 #Javascript
Vue自定义弹窗指令的实现代码
Aug 13 #Javascript
You might like
php socket通信简单实现
2016/11/18 PHP
php输出文字乱码的解决方法
2019/10/04 PHP
实例化php类时传参的方法分析
2020/06/05 PHP
2020最新版 PhpStudy V8.1版本下载安装使用详解
2020/10/30 PHP
Array.slice()与Array.splice()的返回值类型
2006/10/09 Javascript
jQuery 美元符冲突的解决方法
2010/03/28 Javascript
jquery设置元素的readonly和disabled的写法
2013/09/22 Javascript
5分钟理解JavaScript中this用法分享
2013/11/09 Javascript
javascript + jquery实现定时修改文章标题
2014/03/19 Javascript
jQuery中:visible选择器用法实例
2014/12/30 Javascript
Jquery判断radio、selelct、checkbox是否选中及获取选中值方法总结
2015/04/15 Javascript
AngularJS实现单独作用域内的数据操作
2016/09/05 Javascript
NodeJS实现客户端js加密
2017/01/09 NodeJs
超全面的javascript中变量命名规则
2017/02/09 Javascript
JS中图片压缩的方法小结
2017/11/14 Javascript
JavaScript学习笔记之DOM基础操作实例小结
2019/01/09 Javascript
IE11下CKEditor在Bootstrap Modal中下拉问题的解决
2019/09/25 Javascript
vue项目页面嵌入代码块vue-prism-editor的实现
2020/10/30 Javascript
[01:33]一分钟玩转DOTA2第三弹:DOTA2&DotA快捷操作大对比
2014/06/04 DOTA
在Python中使用全局日志时需要注意的问题
2015/05/06 Python
Python中Django框架下的staticfiles使用简介
2015/05/30 Python
读取json格式为DataFrame(可转为.csv)的实例讲解
2018/06/05 Python
python调用支付宝支付接口流程
2019/08/15 Python
关于numpy.where()函数 返回值的解释
2019/12/06 Python
Python 随机生成测试数据的模块:faker基本使用方法详解
2020/04/09 Python
Matplotlib.pyplot 三维绘图的实现示例
2020/07/28 Python
Seavenger官网:潜水服、浮潜、靴子和袜子
2020/03/05 全球购物
先进个人事迹材料
2014/01/25 职场文书
事务机电主管工作职责
2014/02/25 职场文书
求职教师自荐书
2014/06/19 职场文书
个人批评与自我批评范文
2014/10/17 职场文书
优秀团员事迹材料
2014/12/25 职场文书
面试感谢信范文
2015/01/22 职场文书
2015年学生会部门工作总结
2015/04/21 职场文书
团结主题班会
2015/08/13 职场文书
详细聊聊浏览器是如何看闭包的
2021/11/11 Javascript