Vue 项目中使用 AbortController:解决请求取消、超时与内存泄漏问题
·
文章目录
在平时项目开发中,经常遇到这些问题:
- 用户快速切换页面:旧请求还没返回,新页面的请求已经发出,导致页面闪烁或数据错乱。
- 搜索框实时输入:用户频繁输入关键词,旧请求结果覆盖了最新结果。
- 组件卸载时仍在更新状态:报错 “
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 就是请求的“刹车器”,能在项目中随时中止请求,保证性能与体验。
更多推荐
所有评论(0)