vue用addRoutes实现动态路由的示例

之前在基于Vue实现后台系统权限控制一文中提到路由权限的实现思路,因为不喜欢在每次路由跳转的before钩子里做判断,所以在初始化Vue实例前对路由做了筛选,再用实际路由初始化Vue实例,代价是登录页需要从Vue实例中独立出来,实现上倒没什么问题,不过这种做法需要在登录和首页之间通过url跳转,感觉总是不太”优雅”,实际上只要能在登录后动态修改当前实例的路由就行了,之前确实没办法,但vue-router 2.2版本新增了一个router.addRoutes(routes)方法,让动态路由得以实现。

想当然的实现方案

用动态路由实现路由权限控制貌似是一个完美的方案,初始路由只有登录和404,登录后动态添加可用路由,同时将菜单数据保存到Vuex或本地用于实现动态菜单,关键节点大致如下:

//初始路由:
[{
 path: '/login',
 name: 'login',
 component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
 path: '/404',
 name: '404',
 component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
 path: '*',
 redirect: '/404'
}]

//登录逻辑
let vm = this;
axios.get('/login', vm.user).then((res) => {
  let extendsRoutes = filterRoutes(res.menus);
  <!--
  //假设得到的可用路由如下
  [{
   path: '/',
   name: '首页',
   component: (resolve) => require(['../views/index.vue'], resolve),
   children: [{
    path: '/menus',
    name: '菜单管理',
    component: (resolve) => require(['../views/menus.vue'], resolve)
   }, {
    path: '/resources',
    name: '资源管理',
    component: (resolve) => require(['../views/resources.vue'], resolve)
   }]
  }]-->
  //存菜单
  sessionStorage.setItem('menus',JSON.stringify(extendsRoutes[0].children));
  //动态添加路由
  vm.$router.addRoutes(extendsRoutes);
  //跳转到应用界面
  vm.$router.push({path:'/'});
})

//首页获取菜单数据
this.menus = JSON.parse(sessionStorage.getItem('menus'));
//用此数据循环菜单
..

目前为止看上去一切顺利,然而前方有坑。

动态路由的坑

第一个坑是,如果你将这套逻辑实现之后会发现打开应用看到的第一个页面是404,这是因为启动服务后将默认打开首页'/‘,然而初始路由中没有这个路径,因此根据路由规则跳转到了404。我们希望结果当然是跳转到'/login',因此需要对这种情况做判断,在用户登录之前所有请求都要指向'/login',这个判断可以在before钩子里做也可以在根组件里做,建议做在根组件的created回调里,核心代码大概这样:

let isLogin = sessionStorage.getItem('user');
if(!isLogin){
  return this.$router.push({path:'/login'});
}

这时候已经可以顺利登录了,登录后很快就会发现第二个坑,手动刷新页面又会跳到404,这是因为刷新会导致Vue重新实例化,路由也恢复到了初始路由,于是当前路径又被重定向到了404,这个问题的根源是可用路由没有实现持久化,那么可以通过将路由数据存sessionStorage来解决,实例化之前如果检测到本地路由就直接合并路由,像这样:

//检测本地路由
let localRoutes = sessionStorage.getItem('routes');
if(localRoutes){
  router.addRoutes(JSON.parse(localRoutes));
}
//实例化
new Vue({
 el: '#app',
 router,
 render: h => h(App)
});

理论上可以,但实际操作要远比上述代码复杂,因为存在本地的只能是权限数据而不是真实路由,路由在存、取之前都要先根据权限匹配获得,过程还是挺繁琐的,而且必须依赖sessionStorage这种持久存储,没有其他方法。问题就出在这个sessionStorage上,原则上权限只能在内存变量中流转,不能直接暴露到用户可操作的地方,试想只要用户手动修改了sessionStorage里的权限,再刷新一下页面就能突破前端路由控制了,非常的不靠谱。

改进方案

既然不能存本地,那就每次刷新都重新从服务端获取,所以改进后的方案是本地存用户token,每次刷新要凭token从服务端重新获取用户信息和权限,然后动态更新路由,获取权限操作可以跟登录检测一起放在根组件的created回调中进行,确保访问任何路径都会先执行这一步,但因为获取权限是异步操作,在此之前仍然会经过应用初始化,所以还是会遇到404的问题,为此我们只需做一个小调整,将不匹配路径(‘*')跳404的路由从初始路由中移除,动态更新路由时再把这个配置加进去,如下:

let userPath = ...//我们的动态路由
//注入时拼接404处理路由
this.$router.addRoutes(userPath.concat([{
 path: '*',
 redirect: '/404'
}]));

这样就解决了刷新问题,后面还有几个小问题就简单了。

首先是菜单,之前通过$router.options.routes访问路由数据实现动态菜单,但这个数据不是响应式的,无法追踪动态路由的变化,因此我们需要将得到的导航菜单数据存到sessionStorage或Vuex里实现数据共享。

资源权限控制也受到很大的影响,实现较为细致的权限控制需要一个自定义权限验证指令和一个全局验证方法,之前的方案里权限是在Vue实例化之前获取的,所以可以很方便的拿到权限后实现验证方法,然后用验证方法实现自定义指令,再将方法全局混合进Vue,然后实例化,这样实例中的 所有组件都可以使用自定义指令和验证方法;但现在的方案是先实例化再获取权限,实例化之前根本没有权限数据,所以自定义指无法实现,等拿到权限后实现了验证方法,却无法再全局混合了。

这个问题最后也解决了,但解决方案就彻底的”有辱斯文”了,首先是全局方法的实现,直接这么做:

Vue.prototype.has = function(){
  ...
}

使用方式跟全局混合的方法完全一样。

自定义指令的实现本来很头疼,因为全局指令只能在实例化之前实现,但那时候又确实没有权限,不过既然验证方法这么做的话,指令倒是也顺便解决了,像这样:

//权限指令
Vue.directive('has', {
 bind: function(el, binding) {
  if (!Vue.prototype.has(binding.value)) {
   el.parentNode.removeChild(el);
  }
 }
});

神奇的prototype貌似自带惰性效果,可以先注册后实现,具体原因我也不太明白,如过有大牛路过,希望能留下答案。

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

(0)

相关推荐

  • vue-router+vuex addRoutes实现路由动态加载及菜单动态加载

    此案例主要实现了一个功能是,在vue实例首次运行时,在加载了login和404两个路由规则,登录成功后,根据登录用户角色权限获取该角色相应菜单权限,生成新的路由规则添加进去. 做过后台管理系统都一定做过这个功能,在对菜单权限进行粗粒度权限控制的时候,通过角色获取菜单后,异步生成菜单,所以一开始拿到需求的时候,我也以为这和平常的没什么不同,不过做起来就发现了很多问题, 1.vue-router的实例,在new vue实例的时候,就加载了,且必须加载,这个时候,登录路由一定要加载,可是这个时候没有登

  • vue用addRoutes实现动态路由的示例

    之前在基于Vue实现后台系统权限控制一文中提到路由权限的实现思路,因为不喜欢在每次路由跳转的before钩子里做判断,所以在初始化Vue实例前对路由做了筛选,再用实际路由初始化Vue实例,代价是登录页需要从Vue实例中独立出来,实现上倒没什么问题,不过这种做法需要在登录和首页之间通过url跳转,感觉总是不太"优雅",实际上只要能在登录后动态修改当前实例的路由就行了,之前确实没办法,但vue-router 2.2版本新增了一个router.addRoutes(routes)方法,让动态路

  • vue自定义指令和动态路由实现权限控制

    功能概述: 根据后端返回接口,实现路由动态显示 实现按钮(HTML元素)级别权限控制 涉及知识点: 路由守卫 Vuex使用 Vue自定义指令 导航守卫 前端工程采用Github开源项目Vue-element-admin作为模板,该项目地址:Github | Vue-element-admin. 在Vue-element-admin模板项目的src/permission.js文件中,给出了路由守卫.加载动态路由的实现方案,在实现了基于不同角色加载动态路由的功能.我们只需要稍作改动,就能将基于角色加

  • 五步教你用Nginx部署Vue项目及解决动态路由刷新404问题

    目录 步骤一:改端口 步骤二: 打包 步骤三:将dist文件夹上传到服务器上 步骤四:修改nginx.conf(重中之重) 步骤五:重启nginx 总结 期末月前本来部署过一次,昨天部署的时候发现又给忘了,而且出了很多问题,在这统一汇总一下. 步骤一:改端口 在vue.config.js下的devServer把host改成服务器的地址 步骤二: 打包 用npm run build打包,最后是这样的,并且目录下多了个dist文件夹 步骤三:将dist文件夹上传到服务器上 我用的xshell,没办法

  • 基于iview-admin实现动态路由的示例代码

    iview-admin是一个基于vue和iview组件库实现的管理后台前端,本文基于iview-admin最新版本,实现基于权限的动态路由加载. 本文代码可参见:https://github.com/MayBeWrong/iview-admin-dynamic-router 背景: 动态路由:vue的路由,可通过new Router传入路由数组定义实现,也可以通过router.addRoutes实现.通过router.addRoutes动态传入路由定义的方式,称之为动态路由.路由数据可以全部保存

  • vue一步到位的实现动态路由

    目录 静态路由的回顾 动态路由的配置 说一些笔者遇到的问题 后记 最近在写vue项目,需要由后台传来当前用户对应权限的路由表,前端通过调接口拿到后处理(后端处理路由),就是配置vue动态路由啦. 由于错信了一些网上的文章:(,导致在这个问题上耗费了不少时间,想想,还是自己写一篇文章来记录一下那些遇到的坑吧. 接下来手把手记录一下,如何从零开始配置vue动态路由. 首先呢,先看看静态路由的配置,简单预览一下,熟悉的可以直接跳过,说这部分,是为了熟悉一下路由的配置,配置动态路由其实就是把静态路由存到

  • vue router学习之动态路由和嵌套路由详解

    本文主要参考:https://router.vuejs.org/zh-cn/essentials/nested-routes.html 本文的阅读前提是已经能够搭建一个vue前台程序并且运行.如果还么有搭建可以参考文章: http://www.jb51.net/article/111650.htm 好,下面上货. 首先介绍一下动态路由. 动态路由按照我的理解,就是说能够进行页面的跳转,比如说:下面的这个页面中: <template> <div id="app">

  • 全面解析vue router 基本使用(动态路由,嵌套路由)

    路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容.Home按钮  => home 内容, about按钮 => about 内容,也可以说是一种映射. 所以在页面上有两个部分,一个是点击部分,一个是点击之后,显示内容的部分. 点击之后,怎么做到正确的对应,比如,我点击home 按钮,页面中怎么就正好能显示home的内容.这就要在js 文件中配置路由. 路由中有三个基本的概念 route,

  • 修改vue源码实现动态路由缓存的方法

    动态路由 官网解读 :我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件.例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染.那么,我们可以在 vue-router 的路由路径中使用"动态路径参数"(dynamic segment) 来达到这个效果. 即如果你有一个 盘点录入单 路由,但你想通过不同的传不同的 ID 来加载 CheckInputInfo 这个组件,若采用 params 方式,这时只需要在 path 后面配置 /:taskId

  • vue addRoutes实现动态权限路由菜单的示例

    需求 最近接手一个后台管理系统,需要实现导航菜单从后台拉取的效果:根据登录用户的权限不同分别拉出来的导航菜单也不一样,另外可操作的界面也存在区别. 问题 因为后台管理系统是准备使用vue+vue-router+element-ui+vuex的搭配来做的,可是单页应用在进入页面之前就已经将vue-router实例化并且注入vue实例中了,所以在进入登录页面的时候旧没办法在重新定制路由了.接下来各种百之谷之,发现vue-router在2.0版本中提供了addRoutes方法添加路由,希望的曙光出现.

  • vue动态添加路由addRoutes之不能将动态路由存入缓存的解决

    在我不知道vue的路由还可以通过addRoutes动态添加时,我只知道vue的路由都是写死在路由表中的,每当跳转时再去加载相应的路由.直到在一个新公司接到需要根据用户的权限显示不同的菜单的需求时才知道了原来vue-router还有一个addRoutes的API,立马研究了一下. router.addRoutes: 函数签名: router.addRoutes(routes: Array<RouteConfig>) 动态添加更多的路由规则.参数必须是一个符合routes选项要求的数组. 点这里去

随机推荐