最近项目中使用了vue-router的addRoutes这个api,遇到了一个小坑,记录总结一下。javascript
场景复现:
作前端开发的同窗,大多都遇到过这种需求:页面菜单根据用户权限动态生成,一个常见的解决方案是:前端
前端初始化的时候,只挂载不须要权限路由,如登录,注册等页面路由,而后等用户登陆以后,后端返回当前用户的权限表,前端根据这个权限表遍历前端路由表,动态生成用户权限路由,而后使用vue-router提供的addRoutes,将权限路由表动态添加到路由实例中,整个过程大体以下:vue
// 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面板紧接着就报以下错误:
java
纳尼(⊙o⊙)?这是怎么回事呢,第二次登陆也正常登陆了,功能上彷佛没有什么问题,但这个警告从哪里来的呢?对于一个重度强迫症患者来讲,任何警告和报错都是不容许出现的,哪怕功能上没什么问题。ios
捋一捋git
这段警告的意思是说,以上的这几个路由命名重复,存在多个name相同的路由。那么为何会有多个路由名称相同的路由呢?github
让咱们从头捋一下这个错误是怎么来的。首先第一次打开网站登陆的时候是没有问题的,只有当咱们退出登陆,从新登陆的时候,这段警告就来了。而且若是咱们在重复登陆以前刷新一下浏览器而后再登陆,这种警告就不会出现了,很神奇是否是?vue-router
分析一下上面的情景:首先这个警告只会在用户从新登陆的时候出现,登陆的时候咱们作的惟一跟路由相关的事情就是动态添加路由,因此问题确定出在 router.addRoutes(routes)
这里,其次这里又分了两种状况:有刷新和无刷新。在无刷新的状况下会报这个警告,有刷新就不会报这个警告。那么有刷新和无刷新有什么区别呢?axios
咱们很容易就想到,当页面刷新的时候,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,垃圾百度,浪费我好长时间,啥都没找到