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 相关文章推荐
JS是否可以跨文件同时控制多个iframe页面的应用技巧
Dec 16 Javascript
jquery的ajax()函数传值中文乱码解决方法介绍
Nov 08 Javascript
js实现C#的StringBuilder效果完整实例
Dec 22 Javascript
使用Javascript实现选择下拉菜单互移并排序
Feb 23 Javascript
Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
Jul 14 Javascript
JS函数修改html的元素内容,及修改属性内容的方法
Oct 28 Javascript
jQuery插件FusionCharts绘制的3D饼状图效果实例【附demo源码下载】
Mar 03 Javascript
基于Vue2实现的仿手机QQ单页面应用功能(接入聊天机器人 )
Mar 30 Javascript
seajs模块压缩问题与解决方法实例分析
Oct 10 Javascript
浅谈Angular 中何时取消订阅
Nov 22 Javascript
深入学习JavaScript中的bom
May 27 Javascript
Openlayers+EasyUI Tree动态实现图层控制
Sep 28 Javascript
解决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
老机欣赏|中国60年代精品收音机
2021/03/02 无线电
PHP文件操作详解
2016/12/30 PHP
微信公众号开发之获取位置信息php代码
2018/06/13 PHP
(转载)JavaScript中匿名函数,函数直接量和闭包
2007/05/08 Javascript
取选中的radio的值
2010/01/11 Javascript
Extjs 几个方法的讨论
2010/01/28 Javascript
JS声明变量背后的编译原理剖析
2012/12/28 Javascript
判断ie的两种简单方法
2013/08/12 Javascript
javascript中数组的concat()方法使用介绍
2013/12/18 Javascript
JS上传图片前实现图片预览效果的方法
2015/03/02 Javascript
jquery实现多条件筛选特效代码分享
2015/08/28 Javascript
创建自己的jquery表格插件
2015/11/25 Javascript
Javascript中常见的逻辑题和解决方法
2016/09/17 Javascript
seajs实现强制刷新本地缓存的方法分析
2017/10/16 Javascript
js+html5实现手机九宫格密码解锁功能
2018/07/30 Javascript
jQuery+CSS实现的标签页效果示例【测试可用】
2018/08/14 jQuery
javascript 对象 与 prototype 原型用法实例分析
2019/11/11 Javascript
vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)
2020/04/11 Javascript
[01:17:47]TNC vs VGJ.S 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[原创]windows下Anaconda的安装与配置正解(Anaconda入门教程)
2018/04/05 Python
win8.1安装Python 2.7版环境图文详解
2019/07/01 Python
django 实现将本地图片存入数据库,并能显示在web上的示例
2019/08/07 Python
python计算波峰波谷值的方法(极值点)
2020/02/18 Python
python爬虫分布式获取数据的实例方法
2020/11/26 Python
CSS3 制作绽放的莲花采用效果叠加实现
2013/01/31 HTML / CSS
英国女性时尚鞋类的潮流制造者:Koi Footwear
2018/10/19 全球购物
TUMI新加坡官网:国际领先的商旅箱包品牌
2019/01/12 全球购物
物流仓储计划书
2014/01/10 职场文书
机电专业大学生职业规划书范文
2014/02/25 职场文书
公司联欢晚会主持词
2014/03/22 职场文书
课外活动总结
2015/02/04 职场文书
护士自荐信范文
2015/03/25 职场文书
Django使用echarts进行可视化展示的实践
2021/06/10 Python
mysql自增长id用完了该怎么办
2022/02/12 MySQL
Python识别花卉种类鉴定网络热门植物并自动整理分类
2022/04/08 Python
解决IIS7下无法绑定https主机的问题
2022/04/29 Servers