Vue组件销毁时为什么之前的AJAX请求没被取消?

Tr° 鑫丹 阅读 11

在做搜索框实时查询功能时,当我快速输入多个字符导致多次发送请求,虽然用了abortController,但页面跳转时控制台还是报错”AbortError”,之前的请求好像没完全取消。

我的代码是这样写的:


<template>
  <input @input="search">
</template>

<script>
export default {
  data() {
    return {
      controller: null
    }
  },
  methods: {
    search() {
      this.controller = new AbortController()
      fetch(api/search?term=${this.term}, { signal: this.controller.signal })
        .then(...) // 处理响应
      this.controller.abort() // 立即调用取消
    }
  }
}
</script>

问题是我这样写会导致所有请求都被立刻取消,但如果把abort放在setTimeout里又可以正常获取数据。那应该怎样正确在组件销毁时取消未完成的请求呢?

我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
Dev · 文鑫
你现在的写法有几个问题,我来一步步帮你理清楚。首先你要明白,AbortController 的作用是用来取消指定的请求,但你在 search 方法里每次调用都会新建一个 AbortController 实例,并且马上调用 abort,这就导致所有请求都会被立刻取消。这显然是不符合预期的。

其次,页面跳转时控制台报 AbortError,是因为组件销毁时没有正确处理那些还没完成的请求。我们得在组件销毁时手动取消这些请求,同时保证正常的请求流程不会被打断。

下面是一个正确的解决方案,分步骤说明:

第一步,在组件的 data 里维护一个 AbortController 的实例,而不是每次调用 search 时都创建新的实例。这样可以确保同一个控制器管理所有的请求。

第二步,在组件销毁时,也就是 Vue 的 beforeDestroy 生命周期钩子里,调用 abort 来取消所有未完成的请求。这是关键点,因为组件销毁时如果不取消请求,浏览器可能还会尝试处理这些请求,导致报错。

第三步,不要在 search 方法里直接调用 abort,而是只负责发起新请求。每次发起新请求前,先检查是否有旧的控制器存在,如果有就取消旧的请求,然后创建一个新的控制器。

下面是修改后的代码:

export default {
data() {
return {
controller: null // 维护一个 AbortController 实例
}
},
methods: {
search() {
// 如果已经有旧的控制器,先取消之前的请求
if (this.controller) {
this.controller.abort()
}
// 创建新的控制器
this.controller = new AbortController()
const signal = this.controller.signal

// 发起新的请求
fetch(/api/search?term=${this.term}, { signal })
.then(response => {
if (!response.ok) throw new Error('Network response was not ok')
return response.json()
})
.then(data => {
console.log('搜索结果:', data)
// 只有成功的请求才会走到这里
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求被取消了') // 这是正常现象,不用处理
} else {
console.error('请求出错了:', error)
}
})
}
},
beforeDestroy() {
// 组件销毁时取消所有未完成的请求
if (this.controller) {
this.controller.abort()
console.log('组件销毁,取消所有未完成的请求')
}
}
}


解释一下这段代码的逻辑:每次调用 search 方法时,如果已经有一个控制器存在,我们就先调用 abort 取消之前的请求,然后再创建新的控制器发起新请求。当组件销毁时,我们在 beforeDestroy 钩子里再次调用 abort,确保没有任何遗留的请求还在运行。

另外,关于你提到的把 abort 放在 setTimeout 里的做法,虽然看起来能解决问题,但实际上是一种不优雅的 hack 手段。它并不能真正解决组件销毁时的请求取消问题,反而会让代码变得难以维护。所以还是建议按照上面的方式处理。

最后再提醒一点,AbortError 是正常的现象,表示请求被成功取消了,不需要担心这个错误。你只需要在 catch 里判断错误类型,如果是 AbortError 就忽略它即可。

希望这些内容能帮到你,有问题随时问!
点赞 1
2026-02-14 17:26