Vue Router 中“取消导航”是通过调用 next(false) 将导航状态设为 cancelled,中断守卫链执行、阻止后续守卫 resolve,并回滚 URL;但不自动终止异步请求,需手动配合 AbortController 等清理。

路由导航的“取消”不是简单地停止跳转,而是通过中断异步守卫链的执行流来实现的。关键在于 next(false) 的调用时机和守卫解析管道的状态管理——它不终止 JavaScript 执行,而是标记当前导航为“已拒绝”,并阻止后续守卫继续 resolve。
守卫管道如何被“中断”
Vue Router 的导航流程本质是一个 Promise 链式解析过程。每个守卫(如 beforeEach、beforeEnter、beforeRouteLeave)都必须显式调用 next() 来推进流程。一旦某个守卫调用了 next(false):
- 当前导航的内部状态被设为
cancelled,不再进入后续守卫(包括beforeResolve和组件内beforeRouteEnter) - 已启动但尚未 resolve 的异步操作(如
import()加载组件、API 请求)不会自动中止——Router 不会主动 abort fetch 或 cancel Promise - URL 若已变更(如用户点击后退),浏览器历史栈会被回滚到
from对应的位置 -
afterEach不会触发,因为导航未进入 “confirmed” 状态
源码级的关键控制点(vue-router@4.x)
在源码中,核心逻辑位于 navigationGuards.ts 和 router.ts 的导航解析器中:
- 每个导航实例(
Navigation)持有status: 'idle' | 'pending' | 'cancelled' | 'aborted' | 'completed' -
next(false)实际调用的是内部函数abortNavigation(),它将状态置为cancelled并清空待执行的守卫队列 - 全局
beforeEach守卫返回false(无 next 调用)时,等价于隐式调用next(false),同样触发 abort - 组件级
beforeRouteLeave中调用next(false)会提前终止整个导航,甚至跳过还未执行的全局前置守卫(因该守卫已在失活组件之后触发)
为什么异步请求不会自动取消?
Vue Router 的设计原则是“守卫只管导航逻辑,不管业务副作用”。例如:
- 你在
beforeEach中发了一个登录态校验请求,然后调用next(false),这个请求仍在后台运行 - 你在
beforeRouteLeave中启动了表单保存 API,用户点了“取消”,next(false)阻止了跳转,但 API 仍可能成功提交 - 真正需要取消请求,得靠 AbortController、取消 token 或手动清理 pending Promise(比如用
onBeforeUnmount清理未完成的 fetch)
如何安全处理异步守卫中的取消
不能依赖 Router 自动清理,需主动配合:
- 在守卫中使用
AbortSignal:把 signal 传给 fetch,再在next(false)后调用abortController.abort() - 对组件级守卫,用
onBeforeRouteLeave(Composition API)配合watchEffect(onInvalidate => {...})自动清理副作用 - 避免在守卫中直接修改全局状态;若已修改,应在
next(false)分支中做逆向恢复(如还原表单草稿) - 对路由独享守卫
beforeEnter,可结合to.meta.cancelToken = Symbol()做轻量标识,便于业务侧判断是否已被废弃


















