vue-router之解决addRoutes使用遇到的坑


Posted in Javascript onJuly 19, 2020

最近项目中使用了vue-router的addRoutes这个api,遇到了一个小坑,记录总结一下。

场景复现:

做前端开发的同学,大多都遇到过这种需求:页面菜单根据用户权限动态生成,一个常见的解决方案是:

前端初始化的时候,只挂载不需要权限路由,如登陆,注册等页面路由,然后等用户登录之后,后端返回当前用户的权限表,前端根据这个权限表遍历前端路由表,动态生成用户权限路由,然后使用vue-router提供的addRoutes,将权限路由表动态添加到路由实例中,整个过程大致如下:

// router.js 文件
 
// 需要用户权限的路由表
const appRoutes = [
 {
 path: '/dashboard',
 name: 'dashboard',
 component: () => import('...'),
 children: [
 RouteConfig1, 
  RouteConfig2, 
  ...
 ]
 },
 RouteConfig,
 ...
];
 
// 不需要用户权限的路由表
const constantRoutes = [
 {
 path: '/login',
 name: 'login',
 component: Login
 },
 {
 path: '/register',
 name: 'register',
 component: Register
 },
 ...
]
 
// 初始化路由的时候,只挂载不需要用户权限的路由表
const router = new VueRouter({
 mode: 'history',
 base: process.env.BASE_URL,
 constantRoutes
});
 
/** 
 * 
 * 假如后端返回的数据格式如下: 
 * 
 * {
 * status: 200,
 * message: 'successful',
 * data: {
 *  user: {...},
 *  token: '...',
 *  permisssion: [...]
 * }
 * }
 * 
 * login.vue
 */
axios.post('/user/login',{username,password})
 .then(res => {
 if (res.status === 200) {
   // 如果登录成功,则需要遍历生成用户权限路由
  // filterRoutes根据permission和router.js中定义的appRoutes生成动态路由表
   const routes = filterRoutes(permission);
   
   // 然后使用addRoutes将routes挂载到router中
   router.addRoutes(routes);
  } else {
   ...
  }
 })
  .catch(error => { ... })

写到这里,貌似动态生成路由的功能就好了,一切都perfect了,但问题紧接着就来了,当用户登录之后,我们点击页面上的退出按钮退出当前登录,然后重新登录,会发现浏览器console面板紧接着就报如下错误:

纳尼(⊙o⊙)?这是怎么回事呢,第二次登录也正常登录了,功能上似乎没有什么问题,但这个警告从哪里来的呢?对于一个重度强迫症患者来说,任何警告和报错都是不允许出现的,哪怕功能上没什么问题。

捋一捋

这段警告的意思是说,以上的这几个路由命名重复,存在多个name相同的路由。那么为什么会有多个路由名称相同的路由呢?

让我们从头捋一下这个错误是怎么来的。首先第一次打开网站登录的时候是没有问题的,只有当我们退出登录,重新登录的时候,这段警告就来了。并且如果我们在重复登录之前刷新一下浏览器然后再登录,这种警告就不会出现了,很神奇是不是?

分析一下上面的情景:首先这个警告只会在用户重新登录的时候出现,登录的时候我们做的唯一跟路由相关的事情就是动态添加路由,所以问题肯定出在 router.addRoutes(routes)这里,其次这里又分了两种情况:有刷新和无刷新。在无刷新的情况下会报这个警告,有刷新就不会报这个警告。那么有刷新和无刷新有什么区别呢?

我们很容易就想到,当页面刷新的时候,Vue实例会重新初始化,Vue实例初始化的过程中,挂载在它上面的Vue-Router,Store等内容也会重新初始化。而在不刷新的情况下,就不会重新初始化。

再想想,我们第一次登录之后,通过addRoutes添加了权限路由routes到router上,假设我们这个权限routes中包括了dashboard,user,role三个路由,那么当我们退出登录,然后重新登录的时候,由于同一个用户登录,后端返回的权限列表是一样的,生成的动态路由routes也是一样的(即里面同样包含了dashboard,user,role三个路由),那么此时再次添加这三个路由就导致router中挂载的routes重复。而在刷新的情况下,由于router重新初始化,只包含了初始化我们添加的不需要权限的路由,此时再次登录,重新添加就不存在路由重复的问题了。

通过以上的分析,我们搞清了问题的来源,那么如何解决呢,很遗憾,vue-router并没有删除路由的api。根据以上的分析,我们很容易想到,通过强制刷新页面的方式来重置router:即当用户退出登录的时候,通过js强制刷新一下页面。就可以解决问题。这种方式虽然可以解决问题,但显得不是很优雅,而且刷新页面导致资源重新加载和页面闪烁,体验也不是特别好。因此有没有在不刷新的情况下解决问题的办法呢?

经过一番搜索,终于找到了一种方法,即重置当前router的match属性:

router.js

// 定义一个函数来创建router
export const createRouter = routes => new VueRouter({
 mode: 'history',
 base: process.env.BASE_URL,
 routes
});
 
// 在使用addRoutes的地方
// 重置当前router的match = 初始router.match
router.match = createRouter(constantRoutes).match;
router.addRoutes(routes);

这样就可以完美解决问题了。

总结:

整个解决的过程还是比较痛苦的,因为实际中我的代码是比较复杂的,并不像上面简化后那么简单。

整个addRoutes是在store.dispatch中完成,并且中间还夹杂着生成动态路由,根据动态路由再生成用户菜单等一系列功能,干扰比较大,并且这个是源码报警,不好定位,只能通过console和浏览器调试,一步步缩小报错范围,最终找到问题原因。

然后再通过google,以及搜索vue-router仓库的issue一步步找到解决方法。

所以想说,如果大家开发中遇到一些第三方依赖的问题,可以去搜索官方仓库的issue,很好用的,很多问题其实issue中都有答案。我是屡试不爽。

最后,一定要用google,百度,浪费我好长时间,啥都没找到~

Javascript 相关文章推荐
解析arp病毒背后利用的Javascript技术附解密方法
Aug 06 Javascript
jQuery 使用手册(六)
Sep 23 Javascript
JavaScript中的style.display属性操作
Mar 27 Javascript
EasyUi datagrid 实现表格分页
Feb 10 Javascript
jQuery中使用animate自定义动画的方法
May 29 Javascript
js创建数组的简单方法
Jul 27 Javascript
AngularJS模板加载用法详解
Nov 04 Javascript
细说webpack源码之compile流程-入口函数run
Dec 26 Javascript
vue实现图片滚动的示例代码(类似走马灯效果)
Mar 03 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
Aug 08 Javascript
深入解读Node.js中的koa源码
Jun 17 Javascript
jquery实现有过渡效果的tab切换
Jul 17 jQuery
解决vue+router路由跳转不起作用的一项原因
Jul 19 #Javascript
解决vue项目router切换太慢问题
Jul 19 #Javascript
解决elementUI 切换tab后 el_table 固定列下方多了一条线问题
Jul 19 #Javascript
vue2.* element tabs tab-pane 动态加载组件操作
Jul 19 #Javascript
解决vue中el-tab-pane切换的问题
Jul 19 #Javascript
解决echarts图表使用v-show控制图表显示不全的问题
Jul 19 #Javascript
解决Echarts 显示隐藏后宽度高度变小的问题
Jul 19 #Javascript
You might like
PHP入门经历和学习过程分享
2014/04/11 PHP
ThinkPHP3.2.2的插件控制器功能
2015/03/05 PHP
PHP中检查isset()和!empty()函数的必要性
2019/02/13 PHP
jQuery 打造动态渐变按钮 详细图文教程
2010/04/25 Javascript
JavaScript高级程序设计 阅读笔记(四) ECMAScript中的类型转换
2012/02/27 Javascript
JavaScript实现继承的4种方法总结
2014/10/16 Javascript
jQuery获取页面元素绝对与相对位置的方法
2015/06/10 Javascript
javascript弹出窗口中增加确定取消按钮
2016/06/24 Javascript
微信小程序开发之大转盘 仿天猫超市抽奖实例
2016/12/08 Javascript
利用iscroll4实现轮播图效果实例代码
2017/01/11 Javascript
详解微信小程序 wx.uploadFile 的编码坑
2017/01/23 Javascript
如何为vue的项目添加单元测试
2018/12/19 Javascript
JS浅拷贝和深拷贝原理与实现方法分析
2019/02/28 Javascript
Vue+Element实现表格编辑、删除、以及新增行的最优方法
2019/05/28 Javascript
[40:57]TI4 循环赛第二日 iG vs EG
2014/07/11 DOTA
[04:40]2016个国际邀请赛中国区预选赛场地——华西村观战指南
2016/06/25 DOTA
[04:42]5分钟带你了解什么是DOTA2(第一期)
2017/02/07 DOTA
python通过post提交数据的方法
2015/05/06 Python
python实现年会抽奖程序
2019/01/22 Python
python词云库wordCloud使用方法详解(解决中文乱码)
2020/02/17 Python
浅谈Python程序的错误:变量未定义
2020/06/02 Python
Python3压缩和解压缩实现代码
2021/03/01 Python
CSS3 渐变(Gradients)之CSS3 径向渐变
2016/07/08 HTML / CSS
HTML5之HTML元素扩展(上)—新增加的元素及使用概述
2013/01/31 HTML / CSS
html5+svg学习指南之SVG基础知识
2014/12/17 HTML / CSS
德国婴儿推车和儿童安全座椅商店:BABYSHOP
2016/09/01 全球购物
戴森美国官网:Dyson美国
2016/09/11 全球购物
Links of London官方网站:英国标志性的珠宝品牌
2017/04/09 全球购物
Move Free官方海外旗舰店:美国骨关节健康专业品牌
2017/12/06 全球购物
俄罗斯美容和健康网上商店:Созвездие Красоты
2019/07/23 全球购物
十一个高级MySql面试题
2014/10/06 面试题
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
2012/05/30 面试题
机电工程学生自荐信范文
2013/12/07 职场文书
公证书样本
2014/04/10 职场文书
小学网上祭英烈活动总结
2014/07/05 职场文书
公司保洁员岗位职责
2015/02/13 职场文书