abortcontroller需在请求前创建并注册清理函数,每次watcheffect执行时新建实例并立即通过oninvalidate注册abort(),再发起带signal的fetch或axios请求;需区分aborterror/cancelederror与真实错误,且用cancelled标记防止响应覆盖。

AbortController 必须在请求发起前创建并注册清理
每次 watchEffect 重新执行时,都要新建一个 AbortController 实例,并立即将其 abort() 方法通过 onInvalidate 注册为清理函数。这样 Vue 就能在下一次 effect 触发前自动调用它,中断上一个还在进行中的 fetch 或 axios 请求。
关键点:
- 清理函数必须在异步操作(如 fetch)调用之前注册,否则可能错过取消时机
- signal 要作为选项传入请求方法,fetch 和 axios(1.5+)原生支持
- 不要复用 controller 实例,每次都要 new 一个新的
正确结构:声明 → 注册清理 → 发起请求
不能把整个 watchEffect 写成 async 函数,而应在同步回调里手动触发异步逻辑。标准写法是:
- 先声明 controller 变量(如 const controller = new AbortController())
- 立刻调用 onInvalidate(() => controller.abort())
- 再执行 fetch(url, { signal: controller.signal }) 或 axios.get(..., { signal })
这种顺序确保了:旧请求被清除、新请求带信号、清理逻辑绑定到位。
捕获并区分 AbortError 和真实错误
请求被 abort 后,fetch 会 reject 并抛出 AbortError;axios 则抛出 CanceledError 或满足 axios.isCancel(err)。这些不是业务异常,不应计入错误状态或上报。
建议处理方式:
- 在 catch 块中判断错误类型,仅对非取消类错误更新 error 状态
- 可选地记录日志,如 console.debug('请求已取消,不处理')
- 避免把 AbortError 当作网络失败展示给用户
加 cancelled 标记兜底防响应覆盖
即使调用了 abort(),仍存在极小概率:请求已返回但尚未进入 then/catch,此时响应可能误更新到新状态。为保险起见,可配合一个布尔标记:
- 在请求开始前设 let cancelled = false
- onInvalidate 中设 cancelled = true
- then 回调里先判断 if (cancelled) return,再更新数据
这层判断成本极低,却是防止竞态导致 UI 错乱的最后一道防线。










