在 Vue3 项目中通过 AbortController
取消 Axios 请求,可以通过以下 结构化步骤 实现。我们结合组合式 API(Composition API)和现代前端实践演示:
一、基础实现(单个请求)
1. 创建组件逻辑
TypeScript
<script setup>
import { ref, onBeforeUnmount } from 'vue';
import axios from 'axios';
// 存储 AbortController 实例
const abortController = ref(null);
const data = ref(null);
const error = ref(null);
const isLoading = ref(false);
// 发送请求方法
const fetchData = async () => {
isLoading.value = true;
error.value = null;
// 如果存在旧请求,先取消
if (abortController.value) {
abortController.value.abort();
}
// 创建新控制器
abortController.value = new AbortController();
try {
const response = await axios.get('/api/data', {
signal: abortController.value.signal
});
data.value = response.data;
} catch (err) {
// 重点:识别取消错误
if (err.name === 'CanceledError' || axios.isCancel(err)) {
console.log('请求被主动取消');
} else {
error.value = err.message;
}
} finally {
isLoading.value = false;
abortController.value = null; // 清理控制器
}
};
// 组件卸载时自动取消
onBeforeUnmount(() => {
if (abortController.value) {
abortController.value.abort();
}
});
</script>
<template>
<button @click="fetchData" :disabled="isLoading">
{{ isLoading ? '加载中...' : '获取数据' }}
</button>
<button @click="abortController?.abort()" :disabled="!isLoading">
取消请求
</button>
<div v-if="data">{{ data }}</div>
<div v-if="error" class="error">{{ error }}</div>
</template>
二、进阶实现(多个并行请求)
1. 封装可复用 Hook
TypeScript
// src/composables/useAbortController.js
import { onBeforeUnmount } from 'vue';
export default function useAbortController() {
const controllers = new Map(); // 用 Map 存储多个控制器
// 添加控制器
const createSignal = (requestId) => {
const controller = new AbortController();
controllers.set(requestId, controller);
return controller.signal;
};
// 取消特定请求
const abortRequest = (requestId) => {
if (controllers.has(requestId)) {
controllers.get(requestId).abort();
controllers.delete(requestId);
}
};
// 自动清理
onBeforeUnmount(() => {
controllers.forEach(controller => controller.abort());
controllers.clear();
});
return { createSignal, abortRequest };
}
2. 在组件中使用
TypeScript
<script setup>
import { ref } from 'vue';
import axios from 'axios';
import useAbortController from '@/composables/useAbortController';
const { createSignal, abortRequest } = useAbortController();
const userData = ref(null);
const postData = ref(null);
// 并行发送多个请求
const fetchAllData = async () => {
try {
const [usersRes, postsRes] = await Promise.all([
axios.get('/api/users', {
signal: createSignal('users') // 唯一标识
}),
axios.get('/api/posts', {
signal: createSignal('posts')
})
]);
userData.value = usersRes.data;
postData.value = postsRes.data;
} catch (err) {
if (!err.name.includes('Cancel')) {
console.error('请求失败:', err);
}
}
};
// 手动取消用户数据请求
const cancelUsers = () => abortRequest('users');
</script>
<template>
<button @click="fetchAllData">加载全部数据</button>
<button @click="cancelUsers">取消用户数据请求</button>
</template>
三、最佳实践要点
1. 错误处理规范
TypeScript
try {
// ...请求逻辑
} catch (err) {
if (err.name === 'CanceledError' || axios.isCancel(err)) {
console.log('请求取消:', err.message);
return; // 明确终止后续处理
}
// 其他错误处理
if (err.response?.status === 404) {
// 处理 404
} else {
// 通用错误
}
}
2. 自动取消优化
在路由切换时自动取消所有请求:
TypeScript
// src/router.js
import { createRouter } from 'vue-router';
import { useAbortController } from '@/composables/useAbortController';
const router = createRouter({...});
// 路由守卫中全局取消
router.beforeEach(() => {
const { abortAll } = useAbortController();
abortAll(); // 需要扩展 Hook 功能
});
3. TypeScript 类型支持
TypeScript
// types/abort.d.ts
import type { AxiosRequestConfig } from 'axios';
declare module 'axios' {
export interface AxiosRequestConfig {
signal?: AbortSignal;
requestId?: string; // 自定义标识
}
}
四、常见问题解决
1. 取消无效问题
-
检查 Axios 版本 :确保使用
[email protected]+
-
验证信号传递 :确认请求配置中正确传递了
signal
-
避免重复使用控制器:每个请求必须使用新实例
2. 内存泄漏预防
TypeScript
// 在组件卸载时清理
onBeforeUnmount(() => {
abortController.value?.abort();
controllers.clear(); // 对于 Map 存储的情况
});
3. 浏览器兼容性
TypeScript
# 安装 polyfill(如需支持 IE11)
npm install abortcontroller-polyfill
TypeScript
// main.js
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
通过这种设计,你可以实现:
-
✅ 组件级自动请求取消
-
✅ 精细化的多请求管理
-
✅ 符合 Vue3 响应式特性的状态管理
-
✅ 完善的 TypeScript 类型支持