文章目录
问题描述
在使用 Vue.js 的 vue-router
进行编程式路由跳转时,如果多次执行跳转到当前路由(参数不变),会抛出 NavigationDuplicated
的警告错误。这种错误通常发生在用户频繁点击按钮或执行某些操作时,导致多次触发相同的路由跳转。
路由跳转的两种形式
- 声明式导航:通过
<router-link>
标签进行导航,vue-router
底层已经处理好了重复导航的问题,因此不会出现此类警告。 - 编程式导航:通过
this.$router.push
或this.$router.replace
方法进行导航,可能会出现NavigationDuplicated
警告。
问题原因
在 vue-router
3.5.3 及以上版本中,push
和 replace
方法返回一个 Promise
。如果多次执行相同的路由跳转,vue-router
会检测到重复导航,并抛出 NavigationDuplicated
警告。这是为了提醒开发者避免不必要的路由跳转,从而优化应用性能。
解决方案
1. 传递回调函数
通过给 push
方法传递成功和失败的回调函数,可以捕获到当前错误,从而避免警告。
this.$router.push(
{
name: "search",
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() }
},
() => {}, // 成功回调(空函数)
() => {} // 失败回调(空函数)
);
缺点:
- 治标不治本,其他组件中仍需重复添加回调函数。
- 代码冗余,维护成本高。
2. 重写 push
方法(推荐)
通过全局重写 VueRouter
原型上的 push
方法,彻底解决重复导航问题。
// 保存原始的 push 方法
const originalPush = VueRouter.prototype.push;
// 重写 push 方法
VueRouter.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) {
// 若用户手动传递了回调函数,直接调用原始方法
return originalPush.call(this, location, onResolve, onReject);
}
// 若未传递回调函数,捕获 Promise 异常
return originalPush.call(this, location).catch(err => err);
};
优点:
- 一劳永逸,所有组件中均生效。
- 减少代码重复,提升可维护性。
代码实现细节解析
1. 为什么不能直接使用 this.originalPush()
?
-
作用域问题:
originalPush
是一个通过const
定义的变量,保存了原始push
方法的引用。
它并不属于VueRouter
实例的成员属性,因此在重写的push
方法中,this
(指向 VueRouter 实例)无法直接访问originalPush
。
若尝试调用this.originalPush()
,会导致undefined is not a function
错误。 -
正确调用方式:
必须通过originalPush.call(this, ...)
显式绑定this
,确保原始方法在 VueRouter 实例的上下文中执行。
2. this
的指向是什么?
- 在重写的
push
方法中,this
指向 调用该方法的 VueRouter 实例。
例如,当组件中调用this.$router.push()
时:this.$router
是VueRouter
类的实例。- 因此,重写后的
push
方法中的this
即为该实例。
3. call(this)
的作用
-
绑定上下文:
call
方法用于显式指定函数执行时的this
值。
若直接调用originalPush(location)
,原始push
方法中的this
会指向全局对象(如window
或undefined
),导致路由操作失败。 -
类比解释:
可以理解为“借用”原始方法的能力,但明确告诉它操作发生在当前 VueRouter 实例的上下文中,类似于使用他人的工具时指定工作台。
4. 异常捕获的意义
- 通过
.catch(err => err)
捕获Promise
异常,避免未处理的NavigationDuplicated
错误抛出。 - 这样即使重复导航,错误会被静默处理,不会影响用户体验。
其他注意事项
- 路由参数变化:若跳转时参数不同(如
keyword
改变),vue-router
不会视为重复导航。需确保参数传递正确。 - 性能优化:减少不必要的路由跳转,可提升应用性能和响应速度。
总结
通过重写 VueRouter
原型上的 push
方法,能够高效解决编程式导航重复跳转的警告问题。此方法不仅全局生效,还保持了代码的简洁性。