113
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU |
|---|---|
| 这个作业要求在哪里 | https://bbs.csdn.net/topics/619470310 |
| 这个作业的目标 | 个人技术总结——Axios 技术详解与实践 |
| 其他参考文献 | 无 |
Axios 是一个基于 Promise 的 HTTP 客户端,可在浏览器和 Node.js 环境中使用。它用于实现 HTTP 请求(如 GET、POST、PUT、DELETE 等),支持全局配置、拦截器、并发请求和请求取消等功能。
在前端开发中,尤其是使用 Vue 3 + TypeScript 时,Axios 是处理异步请求的主流工具之一。相比 fetch 和原生 XMLHttpRequest,Axios 提供了更友好的 API 和更强大的功能。
我们选择学习 Axios,原因如下:
Axios 的难点主要在于:
通过 npm 安装 Axios:
npm install axios
request.ts首先,封装通用的 Axios 实例 request.ts,提供基础功能,例如全局配置、请求和响应拦截器等。
// src/plugins/request.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
// 创建 Axios 实例
const service: AxiosInstance = axios.create({
baseURL: "http://localhost:8080", // 全局基础 URL
timeout: 15000, // 全局超时时间(毫秒)
});
// 请求拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = localStorage.getItem("token");
if (token) {
config.headers!["Authorization"] = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
if (response.status === 200) {
return response.data;
}
return Promise.reject(new Error("Request failed"));
},
(error) => Promise.reject(error)
);
export default service;
以下是 Axios 提供的常用请求方法,并附有完整示例:
GET 请求用于从服务器获取数据:
const fetchData = async () => {
const response = await axios.get("/api/data", {
params: { id: 1 },
});
console.log(response);
};
POST 请求用于向服务器提交数据:
const submitData = async () => {
const payload = { name: "Test", age: 25 };
const response = await axios.post("/api/data", payload);
console.log(response);
};
PUT 请求通常用于更新数据:
const updateData = async (id: string) => {
const payload = { name: "Updated Name" };
const response = await axios.put(`/api/data/${id}`, payload);
console.log(response);
};
DELETE 请求用于删除资源:
const deleteData = async (id: string) => {
const response = await axios.delete(`/api/data/${id}`);
console.log(response);
};
使用 FormData 对象上传文件:
const uploadFile = async (file: File) => {
const formData = new FormData();
formData.append("file", file);
const response = await axios.post("/api/upload", formData, {
headers: { "Content-Type": "multipart/form-data" },
});
console.log(response);
};
通过 axios.all 批量发送多个请求:
import axios from "@/plugins/request";
const fetchMultipleData = async () => {
const [res1, res2] = await axios.all([
axios.get("/api/data1"),
axios.get("/api/data2"),
]);
console.log(res1, res2);
};
api.ts)在 request.ts 基础上封装 API 接口管理模块 api.ts,并通过 TypeScript 的 DTO 和通用返回类型规范数据结构。
// src/types/common.ts
export interface ApiResponse<T = any> {
code: number;
data: T;
message: string;
}
// src/types/dto.ts
export interface CreateTaskDTO {
name: string;
description: string;
startDate: string;
endDate: string;
}
export interface FacultyDTO {
id: string;
name: string;
}
export interface TaskPageDTO {
id: string;
name: string;
createdAt: string;
}
// src/api/api.ts
import service from "@/plugins/request";
import { ApiResponse } from "@/types/common";
import { CreateTaskDTO, FacultyDTO, TaskPageDTO } from "@/types/dto";
const api = {
createTask(data: CreateTaskDTO) {
return service.request<ApiResponse<void>>({
method: "post",
url: "/api/new-sprout/admin/v1/task/create",
data,
});
},
getFaculty() {
return service.request<ApiResponse<FacultyDTO[]>>({
method: "get",
url: "/api/new-sprout/admin/v1/faculty",
});
},
getTasks() {
return service.request<ApiResponse<TaskPageDTO[]>>({
method: "get",
url: "/api/new-sprout/admin/v1/task/list",
});
},
};
export default api;
<script lang="ts" setup>
import api from "@/api/api";
import { ref } from "vue";
const task = ref({
name: "",
description: "",
startDate: "",
endDate: "",
});
const createTask = async () => {
try {
await api.createTask(task.value);
console.log("Task created successfully");
} catch (error) {
console.error("Failed to create task:", error);
}
};
</script>
<template>
<div>
<input v-model="task.name" placeholder="Task Name" />
<textarea v-model="task.description" placeholder="Description"></textarea>
<button @click="createTask">Create Task</button>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import api from "@/api/api";
import { TaskPageDTO } from "@/types/dto";
const tasks = ref<TaskPageDTO[]>([]);
const fetchTasks = async () => {
try {
const response = await api.getTasks();
tasks.value = response.data;
} catch (error) {
console.error("Failed to fetch tasks:", error);
}
};
onMounted(fetchTasks);
</script>
<template>
<ul>
<li v-for="(task, index) in tasks" :key="index">{{ task.name }}</li>
</ul>
</template>
在使用 Axios 的过程中,我们遇到了一些常见问题。这些问题的解决方案不仅帮助我们优化了代码质量,也为项目的稳定性提供了保障。以下是具体问题的描述与解决过程:
跨域问题是前端开发中常见的难点之一。当浏览器的同源策略检测到前端发送的请求目标与当前页面的 URL 不同源时(即协议、域名或端口号之一不一致),会阻止请求的发起或响应的接收。
在开发环境中,我们可以通过开发服务器代理解决跨域问题。但在生产环境中,后端服务需要配置 CORS(跨域资源共享)来支持跨域请求。如果未正确处理,跨域问题仍然会存在。
开发环境中的解决方法
配置开发服务器的代理功能,使前端与后端的请求路径一致,从而绕过跨域限制:
// vite.config.ts
export default {
server: {
proxy: {
"/api": {
target: "http://localhost:5000", // 后端服务地址
changeOrigin: true,
},
},
},
};
生产环境中的解决方法
后端需要配置 CORS 以允许跨域访问:
在 Node.js (Express) 中:
const express = require("express");
const cors = require("cors");
const app = express();
app.use(
cors({
origin: "http://example.com", // 允许的前端地址
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true, // 是否允许发送 Cookie
})
);
在 Spring Boot 中:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
}
验证跨域是否成功
确保前端在生产环境中可以正常发送跨域请求。如果问题依然存在,可以通过浏览器的开发者工具检查请求头是否包含 Access-Control-Allow-Origin 等正确的 CORS 配置。
在 Axios 的请求和响应拦截器中,如果未正确返回 Promise.reject,会导致错误无法被组件捕获,进而影响正常的错误处理逻辑。
确保在拦截器中使用 Promise.reject 抛出错误,方便调用的组件捕获错误:
// 请求拦截器
axios.interceptors.request.use(
(config) => config,
(error) => Promise.reject(error)
);
// 响应拦截器
axios.interceptors.response.use(
(response) => response,
(error) => {
console.error("Error occurred:", error.response?.data || error.message);
return Promise.reject(error); // 确保错误抛出
}
);
注意:
try...catch 或 .catch() 捕获错误。在用户频繁点击某些按钮时,可能会导致重复发送请求。这不仅增加了服务器的负担,还可能导致意料之外的逻辑错误。
通过为每个请求添加唯一标识,拦截重复请求:
const pendingRequests = new Map();
axios.interceptors.request.use((config) => {
const requestKey = `${config.url}-${config.method}`;
if (pendingRequests.has(requestKey)) {
throw new Error("Duplicate request detected");
}
pendingRequests.set(requestKey, true);
return config;
});
axios.interceptors.response.use(
(response) => {
const requestKey = `${response.config.url}-${response.config.method}`;
pendingRequests.delete(requestKey);
return response;
},
(error) => {
const requestKey = `${error.config.url}-${error.config.method}`;
pendingRequests.delete(requestKey);
return Promise.reject(error);
}
);
在网络环境较差时,请求可能因超时导致用户体验受影响。如果未正确处理超时逻辑,可能会出现长时间等待的情况。
设置请求的超时时间,并在超时情况下提供用户反馈:
// 设置全局超时时间
const service = axios.create({
timeout: 10000, // 超时时间(毫秒)
});
// 捕获超时错误
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.code === "ECONNABORTED") {
console.error("Request timeout:", error.message);
}
return Promise.reject(error);
}
);
在文件上传时,由于文件过大或服务器处理速度较慢,可能导致请求失败。
分片上传
将文件切分成小块,分批上传,后端合并文件。
调整后端设置
确保后端设置了合理的上传文件大小限制。例如,在 Node.js 中可以通过 express-fileupload 进行设置:
const fileUpload = require("express-fileupload");
app.use(
fileUpload({
limits: { fileSize: 50 * 1024 * 1024 }, // 文件大小限制为 50MB
})
);
前端提供反馈
在上传时显示进度条,提示用户等待。
const uploadFile = async (file: File) => {
const formData = new FormData();
formData.append("file", file);
try {
const response = await axios.post("/api/upload", formData, {
onUploadProgress: (progressEvent) => {
const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
console.log(`Upload progress: ${progress}%`);
},
});
console.log("File uploaded successfully:", response);
} catch (error) {
console.error("File upload failed:", error);
}
};
通过对 Axios 的封装与扩展,我们构建了通用的请求管理模块和集中化的 API 管理,这不仅显著提升了代码的复用性与维护性,也为团队协作提供了清晰的接口规范。封装后的模块统一了请求的基础配置、拦截器逻辑和错误处理机制,使得开发流程更加高效和标准化。结合 TypeScript,我们为每个接口定义了 DTO 和返回类型,确保了项目在开发中的类型安全性和数据一致性。这种类型驱动的开发方式,不仅减少了潜在的错误,也通过更好的 IDE 支持提升了开发体验。
在实际使用过程中,我们有效应对了跨域请求、重复请求、超时处理等常见问题,为项目提供了稳定的网络请求支持。同时,通过文件上传优化和错误提示机制可以改善用户的交互体验。Axios 的封装为当前项目带来了显著的价值,同时也为未来的开发积累了重要的经验。通过模块化设计和灵活的扩展能力,Axios 成为了高效开发不可或缺的一部分。