主题
Axios 常见应用场景
Axios 作为一个功能强大的 HTTP 客户端库,在实际项目中有多种应用场景。本文将详细介绍 Axios 的常见使用场景,并提供实用的代码示例和最佳实践。
1. RESTful API 调用
RESTful API 是现代 Web 应用中最常见的数据交互方式,Axios 提供了简洁的方法来调用各种 RESTful 接口。
基础 CRUD 操作
javascript
// 创建资源(CREATE)
async function createUser(userData) {
try {
const response = await axios.post("/api/users", userData);
return response.data;
} catch (error) {
console.error("创建用户失败:", error);
throw error;
}
}
// 获取资源列表(READ)
async function getUsers() {
try {
const response = await axios.get("/api/users", {
params: {
page: 1,
limit: 10,
},
});
return response.data;
} catch (error) {
console.error("获取用户列表失败:", error);
throw error;
}
}
// 更新资源(UPDATE)
async function updateUser(userId, userData) {
try {
const response = await axios.put(`/api/users/${userId}`, userData);
return response.data;
} catch (error) {
console.error("更新用户失败:", error);
throw error;
}
}
// 删除资源(DELETE)
async function deleteUser(userId) {
try {
await axios.delete(`/api/users/${userId}`);
return true;
} catch (error) {
console.error("删除用户失败:", error);
throw error;
}
}批量操作
javascript
// 批量删除用户
async function deleteMultipleUsers(userIds) {
try {
await axios.delete("/api/users/batch", {
data: { userIds }, // 将数据放在请求体中
});
return true;
} catch (error) {
console.error("批量删除用户失败:", error);
throw error;
}
}2. 表单提交与数据验证
在 Web 应用中,表单提交通常需要与后端 API 交互。Axios 可以轻松处理表单数据的提交。
基本表单提交
javascript
// HTML 表单
// <form id="login-form">
// <input type="email" name="email">
// <input type="password" name="password">
// <button type="submit">登录</button>
// </form>
// 表单提交处理
document
.getElementById("login-form")
.addEventListener("submit", async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const userData = Object.fromEntries(formData.entries());
try {
const response = await axios.post("/api/login", userData);
// 处理登录成功
localStorage.setItem("token", response.data.token);
window.location.href = "/dashboard";
} catch (error) {
// 显示错误信息
const errorElement = document.createElement("div");
errorElement.className = "error-message";
errorElement.textContent = error.response?.data?.message || "登录失败";
event.target.appendChild(errorElement);
}
});表单验证与错误处理
javascript
async function submitForm(formData) {
// 前端验证
if (!formData.email.includes("@")) {
throw new Error("请输入有效的邮箱地址");
}
try {
const response = await axios.post("/api/submit", formData, {
// 配置请求头
headers: {
"Content-Type": "application/json",
},
// 设置超时
timeout: 5000,
});
return response.data;
} catch (error) {
// 处理不同类型的错误
if (error.response) {
// 后端返回的验证错误
if (error.response.status === 422) {
const validationErrors = error.response.data.errors;
// 格式化错误信息
const errorMessages = Object.entries(validationErrors)
.map(([field, msgs]) => `${field}: ${msgs.join(", ")}`)
.join("\n");
throw new Error(`验证失败:\n${errorMessages}`);
}
}
throw error;
}
}3. 文件上传与下载
Axios 可以处理文件上传和下载,支持进度监控等高级功能。
单文件上传
javascript
// HTML
// <input type="file" id="file-upload">
const fileInput = document.getElementById("file-upload");
fileInput.addEventListener("change", async () => {
const file = fileInput.files[0];
const formData = new FormData();
formData.append("file", file);
formData.append("description", "用户上传的文件");
try {
const response = await axios.post("/api/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
// 上传进度监控
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(`上传进度: ${percentCompleted}%`);
},
});
console.log("上传成功:", response.data);
} catch (error) {
console.error("上传失败:", error);
}
});文件下载
javascript
async function downloadFile(fileId, fileName) {
try {
const response = await axios.get(`/api/files/${fileId}`, {
responseType: "blob", // 重要:设置响应类型为 blob
});
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("文件下载失败:", error);
}
}大文件分片上传
javascript
async function uploadLargeFile(file) {
const chunkSize = 1024 * 1024; // 1MB 分片
const totalChunks = Math.ceil(file.size / chunkSize);
const fileId = generateUniqueId(); // 生成唯一文件ID
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const start = chunkIndex * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("file", chunk);
formData.append("fileId", fileId);
formData.append("chunkIndex", chunkIndex);
formData.append("totalChunks", totalChunks);
formData.append("fileName", file.name);
try {
await axios.post("/api/upload/chunk", formData, {
onUploadProgress: (progressEvent) => {
const chunkProgress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
const overallProgress = Math.round(
((chunkIndex * chunkSize + progressEvent.loaded) * 100) / file.size
);
console.log(
`分片 ${chunkIndex + 1}/${totalChunks} 进度: ${chunkProgress}%`
);
console.log(`整体进度: ${overallProgress}%`);
},
});
} catch (error) {
console.error(`分片 ${chunkIndex} 上传失败:`, error);
// 可以实现重试逻辑
}
}
// 通知服务器所有分片上传完成
try {
await axios.post("/api/upload/complete", {
fileId,
fileName: file.name,
});
console.log("文件上传完成");
} catch (error) {
console.error("通知服务器完成失败:", error);
}
}4. 认证与授权
在现代 Web 应用中,用户认证和授权是常见需求,Axios 可以很好地处理 Token 管理和认证请求。
JWT Token 管理
javascript
// 创建带有认证配置的 Axios 实例
const authAxios = axios.create({
baseURL: "/api",
});
// 请求拦截器 - 添加认证 Token
authAxios.interceptors.request.use((config) => {
const token = localStorage.getItem("auth_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器 - 处理 Token 过期
authAxios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// 如果是 401 错误且没有重试过
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// 尝试使用刷新 Token 获取新的访问 Token
const refreshToken = localStorage.getItem("refresh_token");
const response = await axios.post("/api/auth/refresh", {
refresh_token: refreshToken,
});
const { access_token, refresh_token } = response.data;
localStorage.setItem("auth_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
// 更新原始请求的 Authorization 头并重试
authAxios.defaults.headers.common[
"Authorization"
] = `Bearer ${access_token}`;
originalRequest.headers.Authorization = `Bearer ${access_token}`;
return authAxios(originalRequest);
} catch (refreshError) {
// 刷新 Token 失败,跳转到登录页
localStorage.removeItem("auth_token");
localStorage.removeItem("refresh_token");
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);登录与注销流程
javascript
// 登录功能
async function login(credentials) {
try {
const response = await axios.post("/api/auth/login", credentials);
const { access_token, refresh_token, user } = response.data;
// 存储认证信息
localStorage.setItem("auth_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
localStorage.setItem("user", JSON.stringify(user));
return { user };
} catch (error) {
throw error.response?.data || error;
}
}
// 注销功能
async function logout() {
try {
// 向服务器发送注销请求
await authAxios.post("/api/auth/logout");
} catch (error) {
console.error("注销请求失败,但仍会清理本地数据:", error);
} finally {
// 清理本地存储的认证信息
localStorage.removeItem("auth_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("user");
}
}5. 数据聚合与批量请求
在复杂应用中,常常需要同时获取多个数据源的数据,Axios 提供了并发请求的支持。
并行请求多个 API
javascript
async function fetchDashboardData() {
try {
// 并行发送多个请求
const [usersResponse, postsResponse, statsResponse] = await Promise.all([
axios.get("/api/users?limit=10"),
axios.get("/api/posts?limit=5"),
axios.get("/api/stats/dashboard"),
]);
// 整合数据
return {
users: usersResponse.data,
posts: postsResponse.data,
stats: statsResponse.data,
};
} catch (error) {
console.error("获取仪表板数据失败:", error);
throw error;
}
}动态批量请求
javascript
async function fetchMultipleResources(resourceIds, resourceType) {
if (!resourceIds.length) return [];
try {
// 为每个 ID 创建一个请求
const requests = resourceIds.map((id) =>
axios.get(`/api/${resourceType}/${id}`)
);
// 等待所有请求完成
const responses = await Promise.all(requests);
// 提取数据
return responses.map((response) => response.data);
} catch (error) {
console.error(`获取 ${resourceType} 资源失败:`, error);
throw error;
}
}
// 使用示例
const articleIds = [1, 2, 3, 4, 5];
const articles = await fetchMultipleResources(articleIds, "articles");请求超时和重试
javascript
// 创建带超时和重试功能的请求函数
async function requestWithRetry(config, retries = 3, delay = 1000) {
try {
const response = await axios({
...config,
timeout: 5000, // 设置超时时间
});
return response;
} catch (error) {
if (retries > 0 && error.code === "ECONNABORTED") {
// 如果超时且还有重试次数,延迟后重试
await new Promise((resolve) => setTimeout(resolve, delay));
console.log(`请求超时,${retries} 次重试剩余,延迟 ${delay}ms 后重试...`);
return requestWithRetry(config, retries - 1, delay * 2); // 指数退避
}
throw error;
}
}
// 使用示例
const data = await requestWithRetry({
url: "/api/critical-data",
method: "get",
});6. 全局请求管理
在大型应用中,通常需要对所有请求进行统一管理,包括错误处理、加载状态、日志记录等。
创建全局请求管理器
javascript
class ApiService {
constructor() {
this.loading = false;
this.requestCount = 0;
// 创建 Axios 实例
this.axios = axios.create({
baseURL: process.env.API_BASE_URL || "/api",
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
this.setupInterceptors();
}
setupInterceptors() {
// 请求拦截器
this.axios.interceptors.request.use(
(config) => {
// 增加请求计数
this.requestCount++;
this.loading = true;
// 可以在这里触发全局加载状态
console.log("请求开始:", config.url);
// 添加认证 token
const token = localStorage.getItem("auth_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// 记录请求日志
if (process.env.NODE_ENV === "development") {
console.log("API Request:", {
url: config.url,
method: config.method,
params: config.params,
data: config.data,
});
}
return config;
},
(error) => {
this.handleRequestEnd();
return Promise.reject(error);
}
);
// 响应拦截器
this.axios.interceptors.response.use(
(response) => {
this.handleRequestEnd();
// 记录响应日志
if (process.env.NODE_ENV === "development") {
console.log("API Response:", {
url: response.config.url,
status: response.status,
data: response.data,
});
}
return response;
},
(error) => {
this.handleRequestEnd();
// 统一错误处理
this.handleError(error);
return Promise.reject(error);
}
);
}
handleRequestEnd() {
this.requestCount--;
if (this.requestCount <= 0) {
this.requestCount = 0;
this.loading = false;
// 可以在这里关闭全局加载状态
console.log("所有请求完成");
}
}
handleError(error) {
// 根据错误类型显示不同的错误信息
if (error.response) {
// 服务器返回错误状态码
switch (error.response.status) {
case 400:
console.error("请求参数错误:", error.response.data.message);
break;
case 401:
console.error("未授权,请重新登录");
// 可以在这里跳转到登录页
break;
case 403:
console.error("禁止访问");
break;
case 404:
console.error("请求的资源不存在");
break;
case 500:
console.error("服务器内部错误");
break;
default:
console.error(`请求失败: ${error.response.status}`);
}
} else if (error.request) {
// 请求已发出但没有收到响应
console.error("网络错误,请检查您的网络连接");
} else {
// 请求配置出错
console.error("请求配置错误:", error.message);
}
}
// 提供各种请求方法
get(url, config) {
return this.axios.get(url, config);
}
post(url, data, config) {
return this.axios.post(url, data, config);
}
put(url, data, config) {
return this.axios.put(url, data, config);
}
delete(url, config) {
return this.axios.delete(url, config);
}
patch(url, data, config) {
return this.axios.patch(url, data, config);
}
// 并发请求
all(promises) {
return Promise.all(promises);
}
}
// 创建单例实例
const apiService = new ApiService();
export default apiService;7. WebSocket 结合使用
虽然 Axios 主要用于 HTTP 请求,但在实际项目中,它经常与 WebSocket 结合使用,处理不同类型的数据交互需求。
javascript
class DataService {
constructor() {
this.api = apiService; // 使用上面创建的 ApiService 实例
this.socket = null;
this.listeners = new Map();
}
// 初始化 WebSocket 连接
initWebSocket() {
const token = localStorage.getItem("auth_token");
this.socket = new WebSocket(`wss://api.example.com/ws?token=${token}`);
this.socket.onopen = () => {
console.log("WebSocket 连接已建立");
};
this.socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
const { type, payload } = data;
// 触发对应的监听器
if (this.listeners.has(type)) {
this.listeners.get(type).forEach((callback) => callback(payload));
}
} catch (error) {
console.error("解析 WebSocket 消息失败:", error);
}
};
this.socket.onclose = () => {
console.log("WebSocket 连接已关闭");
// 可以实现重连逻辑
};
this.socket.onerror = (error) => {
console.error("WebSocket 错误:", error);
};
}
// 订阅事件
subscribe(eventType, callback) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Set());
}
this.listeners.get(eventType).add(callback);
// 返回取消订阅函数
return () => {
this.unsubscribe(eventType, callback);
};
}
// 取消订阅
unsubscribe(eventType, callback) {
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).delete(callback);
// 如果没有监听器了,清理
if (this.listeners.get(eventType).size === 0) {
this.listeners.delete(eventType);
}
}
}
// 发送 WebSocket 消息
sendSocketMessage(type, payload) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({ type, payload }));
} else {
console.error("WebSocket 连接未建立或已关闭");
}
}
// 结合 HTTP 请求和 WebSocket
async fetchInitialData() {
// 使用 Axios 获取初始数据
const initialData = await this.api.get("/api/initial-data");
// 初始化 WebSocket 以获取实时更新
this.initWebSocket();
return initialData.data;
}
}
// 使用示例
const dataService = new DataService();
// 获取初始数据并设置实时更新
async function setupDataFlow() {
const initialData = await dataService.fetchInitialData();
updateUI(initialData);
// 订阅实时更新
const unsubscribe = dataService.subscribe("data_update", (updatedData) => {
updateUI(updatedData);
});
// 组件卸载时取消订阅
// unsubscribe();
}总结
Axios 作为一个功能强大的 HTTP 客户端库,在现代 Web 开发中有广泛的应用场景。无论是简单的 API 调用、复杂的文件传输,还是全局的请求管理,Axios 都能提供简洁而强大的解决方案。
通过灵活运用 Axios 的各种特性,开发者可以构建更加健壮、高效和用户友好的 Web 应用。在实际项目中,建议根据具体需求,结合 Axios 的最佳实践,创建适合项目的 API 服务层,以提高代码的可维护性和可重用性。