Vue组件销毁时为什么之前的AJAX请求没被取消?
在做搜索框实时查询功能时,当我快速输入多个字符导致多次发送请求,虽然用了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里又可以正常获取数据。那应该怎样正确在组件销毁时取消未完成的请求呢?
AbortController的作用是用来取消指定的请求,但你在search方法里每次调用都会新建一个AbortController实例,并且马上调用abort,这就导致所有请求都会被立刻取消。这显然是不符合预期的。其次,页面跳转时控制台报
AbortError,是因为组件销毁时没有正确处理那些还没完成的请求。我们得在组件销毁时手动取消这些请求,同时保证正常的请求流程不会被打断。下面是一个正确的解决方案,分步骤说明:
第一步,在组件的
data里维护一个AbortController的实例,而不是每次调用search时都创建新的实例。这样可以确保同一个控制器管理所有的请求。第二步,在组件销毁时,也就是 Vue 的
beforeDestroy生命周期钩子里,调用abort来取消所有未完成的请求。这是关键点,因为组件销毁时如果不取消请求,浏览器可能还会尝试处理这些请求,导致报错。第三步,不要在
search方法里直接调用abort,而是只负责发起新请求。每次发起新请求前,先检查是否有旧的控制器存在,如果有就取消旧的请求,然后创建一个新的控制器。下面是修改后的代码:
解释一下这段代码的逻辑:每次调用
search方法时,如果已经有一个控制器存在,我们就先调用abort取消之前的请求,然后再创建新的控制器发起新请求。当组件销毁时,我们在beforeDestroy钩子里再次调用abort,确保没有任何遗留的请求还在运行。另外,关于你提到的把
abort放在setTimeout里的做法,虽然看起来能解决问题,但实际上是一种不优雅的 hack 手段。它并不能真正解决组件销毁时的请求取消问题,反而会让代码变得难以维护。所以还是建议按照上面的方式处理。最后再提醒一点,
AbortError是正常的现象,表示请求被成功取消了,不需要担心这个错误。你只需要在catch里判断错误类型,如果是AbortError就忽略它即可。希望这些内容能帮到你,有问题随时问!