更稳妥的做法,是在 beforeDestroy 阶段就主动取消异步请求,而不是等到 destroyed 钩子再处理;如果使用 fetch,可以结合 AbortController.signal,axios 则可使用 CancelToken,并且在 catch 中明确判断是否属于取消异常。同时,最好再配合 this._isDestroyed 做一层兜底,避免组件销毁后请求响应返回时仍去更新状态。

在 Vue 2 中,把未完成异步请求的清理放到 destroyed 钩子里,其实并不是推荐做法。原因很简单:执行到这里时,组件已经处于销毁流程的最后阶段,请求可能早已结束,也可能仍在继续,但此时再去更新数据通常已经没有意义,甚至还可能引发 “Avoid mutating a prop directly” 或 “Cannot set property of null” 之类的控制台警告。更合理、更安全的方式是,在请求发起时就准备好取消机制,并在 beforeDestroy 阶段主动中断请求。
用 AbortController 主动取消 fetch 请求
现代浏览器原生支持 AbortController,这是清理未完成 fetch 请求最直接也最标准的方案:
- 在
data中声明abortController: null - 在
mounted或created发起请求前创建控制器:this.abortController = new AbortController() - 将
signal传入fetch配置项:fetch(url, { signal: this.abortController.signal }) - 在
beforeDestroy中调用this.abortController.abort(),触发AbortError并终止当前请求
axios 请求需配合 CancelToken(Vue 2 兼容方案)
如果项目使用 axios ≤ 0.20.x,可以通过 CancelToken 实现类似的请求取消效果:
- 在 data 中定义
cancelTokenSource: null - 发起请求前创建实例:
this.cancelTokenSource = axios.CancelToken.source() - 在请求配置中加入:
cancelToken: this.cancelTokenSource.token beforeDestroy中执行:if (this.cancelTokenSource) this.cancelTokenSource.cancel('Component destroyed')- 注意在
catch中判断是否为取消错误:if (axios.isCancel(error)) { /* 忽略 */ } else { /* 处理真实错误 */ }
避免依赖 destroyed 做清理
destroyed 钩子执行时,组件实例基本已经解绑,this.$data、this.$el 等对象都不再可靠,同时它也无法阻止 Promise.then 继续执行。即使你在这里调用 abort(),很多情况下也已经晚于请求响应返回:
- 响应到达后如果继续尝试
this.xxx = ...,Vue 已无法正常响应,控制台还可能报错 - 定时器、事件监听器等副作用资源,也应该统一在
beforeDestroy中清理,而不是拖到destroyed destroyed更适合做日志记录、销毁埋点上报等无副作用操作
补充:Promise 状态不可逆,需逻辑兜底
即使请求已经被中止,Promise 本身仍然会进入 catch 分支。因此在 Vue 2 异步请求处理时,业务代码还需要做好额外兜底:
- 不要在
then中直接修改响应式数据,先判断组件是否仍然存活:if (!this._isDestroyed) this.data = res - Vue 2 提供
this._isDestroyed这个内部属性(虽然不是公开 API,但通常较稳定),可用于安全判断 - 更稳健的做法是封装统一的请求函数,自动跳过已销毁组件上的赋值和状态更新操作
