Vue 项目中使用 AbortController:解决请求取消、超时与内存泄漏问题
文章目录
- 一、基础概念
- 二、实用特性与 Vue 项目示例
- 1. 中止单个请求(搜索输入场景)
- 2. 一个信号控制多个请求(页面卸载时统一中止)
- 3. 组件卸载时中止请求(避免内存泄漏)
- 4. 超时自动取消请求
- 5. 封装请求工具,便于全局使用
- 三、总结
在平时项目开发中,经常遇到这些问题:
- 用户快速切换页面:旧请求还没返回,新页面的请求已经发出,导致页面闪烁或数据错乱。
- 搜索框实时输入:用户频繁输入关键词,旧请求结果覆盖了最新结果。
- 组件卸载时仍在更新状态:报错 “
Cannot update state on an unmounted component
”。 - 长耗时请求:用户想手动取消,或需要设置超时时间,避免页面卡死。
- 批量请求管理困难:进入一个页面时要请求多个接口,但离开页面时要统一取消。
这些场景如果不处理好,不仅影响性能,还会带来糟糕的用户体验。
幸运的是,AbortController
提供了优雅的解决方案。
一、基础概念
AbortController
:通过new AbortController()
创建控制器。signal
:使用controller.signal
传递给需要中止的请求。abort()
:调用后,所有绑定该信号的请求都会立即中止。
二、实用特性与 Vue 项目示例
1. 中止单个请求(搜索输入场景)
👉 应用场景:防止旧搜索结果覆盖新结果。
<script setup>
import { ref } from "vue";let controller = null;
const results = ref([]);async function search(keyword) {// 中止上一个请求if (controller) controller.abort();controller = new AbortController();const { signal } = controller;try {const res = await fetch(`/api/search?q=${keyword}`, { signal });results.value = await res.json();} catch (err) {if (err.name === "AbortError") {console.log("请求被中止:", keyword);}}
}
</script><template><div><input type="text" @input="e => search(e.target.value)" placeholder="搜索..." /><ul><li v-for="item in results" :key="item.id">{{ item.name }}</li></ul></div>
</template>
2. 一个信号控制多个请求(页面卸载时统一中止)
👉 应用场景:进入新页面时,老页面的所有请求都自动中止。
<script setup>
import { onBeforeUnmount, ref } from "vue";const user = ref(null);
const messages = ref([]);
const notifications = ref([]);
const controller = new AbortController();
const { signal } = controller;async function loadData() {try {const [u, m, n] = await Promise.all([fetch("/api/user", { signal }),fetch("/api/messages", { signal }),fetch("/api/notifications", { signal })]);user.value = await u.json();messages.value = await m.json();notifications.value = await n.json();} catch (err) {if (err.name === "AbortError") {console.log("所有请求被中止");}}
}loadData();// 页面卸载时统一中止
onBeforeUnmount(() => controller.abort());
</script><template><div><h2>{{ user?.name }}</h2><p>消息数量: {{ messages.length }}</p><p>通知数量: {{ notifications.length }}</p></div>
</template>
3. 组件卸载时中止请求(避免内存泄漏)
👉 应用场景:Vue 组件卸载时防止“状态更新到已销毁的组件”报错。
<script setup>
import { onBeforeUnmount, ref } from "vue";const user = ref(null);
const controller = new AbortController();fetch("/api/user", { signal: controller.signal }).then(res => res.json()).then(data => (user.value = data)).catch(err => {if (err.name === "AbortError") {console.log("请求被中止:组件卸载");}});// 卸载时中止
onBeforeUnmount(() => controller.abort());
</script><template><div><h2 v-if="user">{{ user.name }}</h2><p v-else>加载中...</p></div>
</template>
4. 超时自动取消请求
👉 应用场景:请求超时自动取消,避免长时间无响应。
<script setup>
async function fetchWithTimeout(url, timeout = 5000) {const controller = new AbortController();const { signal } = controller;const timer = setTimeout(() => controller.abort(), timeout);try {const res = await fetch(url, { signal });return await res.json();} finally {clearTimeout(timer);}
}fetchWithTimeout("/api/slow", 3000).then(data => console.log("成功:", data)).catch(err => {if (err.name === "AbortError") {console.log("请求超时,已中止");}});
</script>
5. 封装请求工具,便于全局使用
// utils/http.js
export class HttpClient {constructor() {this.controller = null;}request(url, options = {}) {if (this.controller) this.controller.abort();this.controller = new AbortController();return fetch(url, { ...options, signal: this.controller.signal });}cancel() {if (this.controller) this.controller.abort();}
}
/// Vue中
<script setup>
import { HttpClient } from "@/utils/http";const client = new HttpClient();async function loadData() {try {const res = await client.request("/api/data");console.log(await res.json());} catch (err) {if (err.name === "AbortError") {console.log("请求被取消");}}
}// 手动取消
setTimeout(() => client.cancel(), 1000);
</script>
三、总结
在项目中,AbortController
主要能解决以下问题:
- 搜索输入时中止旧请求 → 保证数据正确。
- 统一中止多个请求 → 页面切换时清理资源。
- 组件卸载时中止请求 → 避免内存泄漏与报错。
- 超时自动取消 → 提升用户体验。
- 工具类封装 → 增强代码复用性和可维护性。
一句话记忆:
AbortController
就是请求的“刹车器
”,能在项目中随时中止请求,保证性能与体验。