有时候你在vue项目中做路由跳转时,可能是比如elementUI的memn同级跳转,也可能是比如root.vue中做身份校验跳转,总之,可能会遇到这样的报错:
Uncaught (in promise) NavigationDuplicated:...
你说他难看吧,也确实难看。但要说是个错误吧,也不影响正常逻辑。但是有错误还是要解决的。
这个问题其实是vue-router3版本更新后和老的写法之间的兼容问题。其实有三种解决方法。网上的解决方案几乎都是第一种,但这样是会出问题的!
先说导致问题的原因:
然后,怎么解决?
弄清了其原因在于“Vue-router
在3.1之后把$router.push()
方法改为了Promise。所以假如没有回调函数,错误信息就会交给全局的路由错误处理,因此就会报上述的错误”之后,就有三种处理方案了:
网上大部分文章都是一上来就放代码:
const originalPush = VueRouter.prototype.push VueRouter.prototype.push = function push(location, onResolve, onReject) { if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject) return originalPush.call(this, location).catch(err => err) }
,,,怎么说呢,至少我做的项目中replace的情况比push多。
其实就是重写 —— replace
和push
方法。笔者查阅官方文档时发现了一个API,似乎就是为了防止这种情况的发生:isNavigationFailure。
所以我觉得可以在index.js/router的单独路由文件中放这样一段代码:
const originalPush = VueRouter.prototype.push const originalReplace = VueRouter.prototype.replace // 重写! VueRouter.prototype.push = function push(location, onResolve, onReject) { // 如果指定了成功或者失败的回调 if (onResolve || onReject) { return originalPush.call(this, location, onResolve, onReject) } // 没有指定成功或者失败的回调,要用catch处理 return originalPush.call(this, location).catch((err) => { // 如果是重复导航产生的错误,不再向外传递错误 if (VueRouter.isNavigationFailure(err)) { return err } // 如果不是重复导航的错误,将错误向下传递 return Promise.reject(err) }) } VueRouter.prototype.replace = function (location, onResolve, onReject) { if (onResolve || onReject) { return originalReplace.call(this, location, onResolve, onReject) } return originalReplace.call(this, location).catch((err) => { if (VueRouter.isNavigationFailure(err)) { return err } return Promise.reject(err) }) }
或者简单一点,这样写:
const originalPush = VueRouter.prototype.push; const originalReplace = VueRouter.prototype.replace; VueRouter.prototype.push = function push(location, onResolve, onReject) { if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject) return originalPush.call(this, location).catch(err => err) } VueRouter.prototype.replace = function push(location, onResolve, onReject) { if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject) return originalReplace.call(this, location).catch(err => err) }
这段代码在笔者遇到的(也就是第三种)场景下,会直接在两个页面之间一直重定向,最终导致页面卡死。
我不知道是不是因为微前端架构中多次触发的原因,因为我的组件中还有生命周期的存在。
既然是因为vue-router版本更新的问题,那我们直接采用老版本不就解决问题了?
固定vue-router版本到3.0.7以下:
npm i vue-router@3.0 -S
但这不是一劳永逸的办法:随着时间的过去,这个版本一定会成为“过老的版本”,而且promise目前来看一定会被越来越多的应用。所以这个办法非常不建议使用!
可以看到,第一种方案在全局进行,写完以后项目的每个地方都能照顾到。但是很遗憾笔者的项目里不能用。但是vue-router的开发者给出了另一个解决方法 —— 为每一个replace
或push
API 增加回调函数,其原理依然是“捕获,但不暴露”:
this.$router.replace/push("xxx").catch(err => {err})