马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
在现代前端开发中,HTTP 请求是与后端服务交互的核心。一个功能全面、高度可扩展的请求模块可以显著提拔开发服从和代码的可维护性。本文将从零开始,利用 Axios 封装一个机动的通用请求模块,涵盖 请求底子、动态参数处理、错误处理、上传文件、请求取消 等功能。
axios 官网中文文档指南
为什么选择 Axios 而不是 Fetch?
虽然 Fetch 是欣赏器内置的原生 API,但它在现实开发中的利用存在一些局限性。以下是 Axios 相较于 Fetch 的重要优势:
功能点AxiosFetch欣赏器兼容性支持 IE 和旧版欣赏器不支持 IE,需引入 Polyfill请求拦截器内置支持,便于统一处理请求逻辑需要手动封装拦截器相应拦截器内置支持,便于统一处理相应逻辑需要手动封装拦截器错误处理自动将 HTTP 错误状态码转为 catch 捕捉必须手动查抄 response.ok自动序列化 JSON请求体自动序列化,相应体自动解析为对象需要手动调用 JSON.stringify 和 JSON.parse请求取消功能内置 CancelToken 功能原生不支持超时配置内置支持必须通过 AbortController 手动实现文件上传内置支持 FormData需手动设置 Content-Type 从这些对比可以看出,Axios 在开发体验和功能的便利性上更胜一筹,尤其适合需要统一管理请求逻辑、错误处理、以及复杂场景(如文件上传和请求取消)的项目。
安装 Axios
在项目中利用 Axios 前,需要先安装它。以下是安装 Axios 的方法:
1. 利用 npm 安装
2. 利用 yarn 安装
封装目标
我们希望封装的请求模块具备以下功能:
- 支持多种请求方法:如 GET、POST、PUT、DELETE 等。
- 动态参数处理:支持 params(查询参数)和 data(请求体数据)。
- 上传文件功能:支持通过 FormData 上传文件。
- 请求取消功能:防止重复请求,或在组件卸载时取消未完成的请求。
- 请求超时控制:可自界说超时时间。
- 失败自动重试:支持自界说重试次数和间隔。
- 全局 Loading 支持:统一展示和隐藏加载动画。
- 统一错误处理:集中处理常见错误,如鉴权失败(401)、服务器错误(500)等。
完整代码实现
以下是封装的 Axios 请求模块的完整代码。
1. 创建 Axios 实例
创建一个 Axios 实例,并设置底子配置:
- import axios from 'axios';
- // 根据环境动态设置 baseURL
- const baseURL =
- process.env.NODE_ENV === 'production'
- ? 'https://api.production.com'
- : 'http://localhost:3000';
- // 创建 Axios 实例
- const axiosInstance = axios.create({
- baseURL, // 基础 API 地址
- timeout: 10000, // 超时时间设置(毫秒)
- withCredentials: true, // 跨域时携带 cookie
- headers: {
- 'Content-Type': 'application/json', // 默认请求头
- },
- responseType: 'json', // 默认响应类型
- });
- export default axiosInstance;
复制代码 2. 请求取消功能
防止重复请求,或在组件卸载时取消未完成的请求:
- const CancelToken = axios.CancelToken;
- const pendingRequests = new Map(); // 存储未完成的请求
- /**
- * 添加请求到 pendingRequests 中
- */
- const addPendingRequest = (config) => {
- const requestKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}:${JSON.stringify(config.data)}`;
- if (!pendingRequests.has(requestKey)) {
- config.cancelToken = new CancelToken((cancel) => {
- pendingRequests.set(requestKey, cancel);
- });
- }
- };
- /**
- * 移除请求
- */
- const removePendingRequest = (config) => {
- const requestKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}:${JSON.stringify(config.data)}`;
- if (pendingRequests.has(requestKey)) {
- const cancel = pendingRequests.get(requestKey);
- cancel(`请求已取消:${requestKey}`); // 执行取消操作
- pendingRequests.delete(requestKey);
- }
- };
- /**
- * 清空所有请求
- */
- const clearPendingRequests = () => {
- pendingRequests.forEach((cancel) => cancel());
- pendingRequests.clear();
- };
复制代码 可以在需要取消请求的场景(如组件卸载时)调用 clearPendingRequests()。
3. 上传文件功能
支持通过 FormData 上传文件
- /**
- * 上传文件
- * @param {string} url 上传文件的接口地址
- * @param {Object} files 文件数据对象(支持多个文件)
- * @param {Object} [extraData] 额外的表单数据
- * @param {Object} [headers={}] 自定义请求头
- * @returns {Promise} 返回一个 Promise
- */
- const uploadFile = (url, files, extraData = {}, headers = {}) => {
- const formData = new FormData();
- // 将文件和额外数据添加到 FormData
- Object.keys(files).forEach((key) => {
- formData.append(key, files[key]);
- });
- Object.keys(extraData).forEach((key) => {
- formData.append(key, extraData[key]);
- });
- return axiosInstance.post(url, formData, {
- headers: {
- 'Content-Type': 'multipart/form-data',
- ...headers,
- },
- });
- };
复制代码 4. 通用请求方法封装
支持动态参数、错误处理、失败重试等功能:
- /**
- * 通用请求方法封装
- * @param {Object} options 请求配置选项
- * @param {string} options.url 请求地址
- * @param {string} [options.method='GET'] 请求方法
- * @param {Object} [options.params={}] 查询字符串参数
- * @param {Object} [options.data={}] 请求体数据
- * @param {Object} [options.headers={}] 自定义请求头
- * @param {boolean} [options.showLoading=false] 是否显示全局 Loading
- * @param {number} [options.retries=0] 请求失败时的重试次数
- * @param {number} [options.retryDelay=1000] 重试间隔时间(毫秒)
- * @param {number} [options.timeout=10000] 请求超时时间(毫秒)
- * @returns {Promise} 返回一个 Promise
- */
- const request = async ({
- url,
- method = 'GET',
- params = {},
- data = {},
- headers = {},
- showLoading = false,
- retries = 0,
- retryDelay = 1000,
- timeout = 10000,
- }) => {
- const config = { url, method, params, data, headers, timeout };
- const sendRequest = async (retryCount) => {
- try {
- removePendingRequest(config); // 防止重复请求
- addPendingRequest(config); // 添加当前请求
- const response = await axiosInstance(config);
- return response.data;
- } catch (error) {
- if (retryCount > 0) {
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
- return sendRequest(retryCount - 1); // 递归重试
- }
- throw error;
- } finally {
- removePendingRequest(config); // 请求完成后移除
- }
- };
- if (showLoading) showGlobalLoading(); // 显示 Loading
- try {
- return await sendRequest(retries);
- } finally {
- if (showLoading) hideGlobalLoading(); // 隐藏 Loading
- }
- };
- export { request, uploadFile, clearPendingRequests };
复制代码 5. 请求拦截器
在请求发送前,添加拦截器来实现以下功能:
- 动态添加 Token:从当地存储中读取 token,并添加到请求头中。
- 开启全局 Loading:通过某种机制触发全局加载动画。
- axiosInstance.interceptors.request.use(
- (config) => {
- // 动态添加 Token
- const token = localStorage.getItem('token');
- if (token) {
- config.headers['Authorization'] = `Bearer ${token}`;
- }
- // 开启全局 Loading
- if (config.showLoading) {
- showGlobalLoading(); // 假设有全局 Loading 的控制函数
- }
- return config;
- },
- (error) => {
- // 请求错误处理
- return Promise.reject(error);
- }
- );
复制代码 6. 相应拦截器
在相应阶段,统一处理以下环境:
- 请求成功:解析相应数据,并根据业务状态码处理逻辑。
- 请求失败:如 401 未登录、403 无权限、500 服务器错误等。
- axiosInstance.interceptors.response.use(
- (response) => {
- // 隐藏全局 Loading
- hideGlobalLoading(); // 假设有全局 Loading 的控制函数
- // 根据业务状态码处理逻辑
- const { code, message, data } = response.data;
- if (code === 200) {
- // 请求成功
- return data;
- } else {
- // 业务错误
- console.error(`业务错误:${message}`);
- return Promise.reject(new Error(message));
- }
- },
- (error) => {
- // 隐藏全局 Loading
- hideGlobalLoading();
- // 统一处理 HTTP 错误状态码
- if (error.response) {
- const { status } = error.response;
- switch (status) {
- case 401:
- console.error('未登录,请重新登录');
- // 跳转到登录页面
- break;
- case 403:
- console.error('无权限访问');
- break;
- case 500:
- console.error('服务器错误,请稍后重试');
- break;
- default:
- console.error('请求失败');
- }
- }
- return Promise.reject(error);
- }
- );
复制代码 利用示例
1. 发起 GET 请求
- import { get } from './request';
- get('/api/users', { page: 1, size: 10 }, true)
- .then((data) => console.log('成功:', data))
- .catch((error) => console.error('失败:', error));
复制代码 2. 发起 POST 请求
- import { post } from './request';
- post('/api/login', { username: 'admin', password: '123456' }, true)
- .then((data) => console.log('登录成功:', data))
- .catch((error) => console.error('登录失败:', error));
复制代码 总结
通过对 Axios 请求模块的封装,我们成功实现了一个功能全面、机动易用的通用请求工具。该工具不但满意了常见的开发需求,还具备了高扩展性和可维护性。其核心功能包括:
- 多种请求方法支持,满意常见的 GET、POST、PUT、DELETE 等请求场景。
- 动态参数和文件上传支持,机动适配复杂的请求需求。
- 请求取消功能,有效避免重复请求,同时在组件卸载时清算未完成的请求。
- 失败自动重试机制,通过配置重试次数和间隔,提拔请求的稳定性。
- 全局 Loading 管理,为用户提供统一的加载动画体验。
- 统一错误处理,集中处理 HTTP 错误和业务状态码,淘汰重复代码。
- 请求超时控制,有效防止长时间无相应的请求占用资源。
在现实开发中,这个封装模块可以极大地提拔代码复用性和团队协作服从,同时淘汰手动处理请求的复杂度。如果有更多个性化需求,也可以在此底子上进一步扩展,如支持更多的请求级功能(例如请求优先级、队列管理等)。
通过这样的封装,我们不但办理了开发中的常见痛点,也为后续的功能扩展提供了强盛的底子。希望这份封装工具能够帮助您更高效地完成项目开发!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |