应在 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 请求最直接、标准的方式:
data 中声明 abortController: null
mounted 或 created 发起请求前新建控制器:this.abortController = new AbortController()
signal 传入 fetch 选项:fetch(url, { signal: this.abortController.signal })
beforeDestroy 中调用 this.abortController.abort(),触发 AbortError 并终止请求若使用 axios ≤ 0.20.x,可用 CancelToken 实现类似效果:
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 钩子执行时,组件实例已解绑,this.$data、this.$el 等均不可靠,且无法阻止 Promise.then 的执行。即使你在里面调用 abort(),也大概率晚于请求响应:
this.xxx = ...,Vue 已不响应,控制台报错beforeDestroy 清理,而非拖到 destroyed
destroyed 仅适合记录日志、上报销毁埋点等无副作用操作即使中止了请求,Promise 本身仍会进入 catch 分支。因此业务代码中要确保:
then 中直接修改响应式数据,先校验组件是否还存活:if (!this._isDestroyed) this.data = res
this._isDestroyed 内部属性(非公开但稳定可用),可用于安全判断