vue-router 之NavigationDuplicated error

950 阅读2分钟

项目中vue-router升级到3.1.x版本后, 在路由跳转通过编程式时控制台如有如下错误:

问题复现

  • 链接: jsrun.pro/KuWKp/edit
  • 复现步骤:
    1. 启用vue-router@3.1.x脚本引用, 并打开浏览器控制台
    2. 点击两次Go to foo 按钮(多次使用$router.replace$router.push跳转到同一地址)

关于这个问题, vuejs成员posva已经在No stacktrace on NavigationDuplicated error中进行说明.

我们来看下他都说了啥:

  1. 3.1.x 版本之前, 在调用router.push方法时没有提供回调, 错误会被发送至全局错误处理方法( global router error handler), 3.1.x版本之后, 调用pushreplace会返回一个promise, 如果导航失败(任何导航取消比如next(false)或next('/other')都算在内)没有被捕获, 你都会在控制台看到这个错误, 这是因为promise的rejection没有被捕获, 因此, 当我们尝试导航到与当前位置相同的路由时这个错误就会出现(路由不需要跳转, 所以orimise处于rejected状态, 而错误有没有手动处理)
  2. 过编程式导航来进行路由跳转时我们可以通过捕获rejection来忽略该错误.router.push('/location', () => {})
  3. 如果不关心导航失败,则应该使用catch来捕获它:router.push('/location').catch(err => {})
  4. 因为Promise api可能会成为默认api,而回调版本会被弃用。
  5. 这不是一个破坏性改动(因为代码仍能够正常运行), 只是有以下使用方式时会有错误提示:
   return router.push('/location') // this is now returning the promise
   await router.push('/location') // this explicitely awaits for the navigation to be finished
  1. 3.1 版本之前router.push不会返回任何内容, 所以上述使用方法属于无效的用例(3.1之前). 3.1之后, 它们是可行的,但是也会在暴露以前不可见的错误.
  2. 如果要全局处理, 可以通过修改Router原型链上的replace, push方法来避免出现该错误.
    ['push', 'replace'].forEach(method => {
        const prototypeMethod = Router.prototype[method];
        Router.prototype[method] = function(location, onResolve, onReject) {
            if (onResolve || onReject) return prototypeMethod.call(this, location, onResolve, onReject);
            return prototypeMethod.call(this, location).catch(err => err);
        };
    });
  1. 不推荐这样做, 因为router.push总是会变成resolve即使导航失败.
  2. 关联issue #2872, #2873, 这些issues与提升Rejection的堆栈追溯有关.