IT评测·应用市场-qidao123.com技术社区

标题: Axios 实战:拦截器、封装与工作中的关键应用 [打印本页]

作者: 立山    时间: 2025-3-15 18:33
标题: Axios 实战:拦截器、封装与工作中的关键应用
在现代前端开发中,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 安装

  1. npm install axios
复制代码
2. 利用 yarn 安装

  1. yarn add axios
复制代码
封装目标

我们希望封装的请求模块具备以下功能:

完整代码实现

以下是封装的 Axios 请求模块的完整代码。
1. 创建 Axios 实例

创建一个 Axios 实例,并设置底子配置:
  1. import axios from 'axios';
  2. // 根据环境动态设置 baseURL
  3. const baseURL =
  4.   process.env.NODE_ENV === 'production'
  5.     ? 'https://api.production.com'
  6.     : 'http://localhost:3000';
  7. // 创建 Axios 实例
  8. const axiosInstance = axios.create({
  9.   baseURL, // 基础 API 地址
  10.   timeout: 10000, // 超时时间设置(毫秒)
  11.   withCredentials: true, // 跨域时携带 cookie
  12.   headers: {
  13.     'Content-Type': 'application/json', // 默认请求头
  14.   },
  15.   responseType: 'json', // 默认响应类型
  16. });
  17. export default axiosInstance;
复制代码
2. 请求取消功能

防止重复请求,或在组件卸载时取消未完成的请求:
  1. const CancelToken = axios.CancelToken;
  2. const pendingRequests = new Map(); // 存储未完成的请求
  3. /**
  4. * 添加请求到 pendingRequests 中
  5. */
  6. const addPendingRequest = (config) => {
  7.   const requestKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}:${JSON.stringify(config.data)}`;
  8.   if (!pendingRequests.has(requestKey)) {
  9.     config.cancelToken = new CancelToken((cancel) => {
  10.       pendingRequests.set(requestKey, cancel);
  11.     });
  12.   }
  13. };
  14. /**
  15. * 移除请求
  16. */
  17. const removePendingRequest = (config) => {
  18.   const requestKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}:${JSON.stringify(config.data)}`;
  19.   if (pendingRequests.has(requestKey)) {
  20.     const cancel = pendingRequests.get(requestKey);
  21.     cancel(`请求已取消:${requestKey}`); // 执行取消操作
  22.     pendingRequests.delete(requestKey);
  23.   }
  24. };
  25. /**
  26. * 清空所有请求
  27. */
  28. const clearPendingRequests = () => {
  29.   pendingRequests.forEach((cancel) => cancel());
  30.   pendingRequests.clear();
  31. };
复制代码
可以在需要取消请求的场景(如组件卸载时)调用 clearPendingRequests()。
3. 上传文件功能

支持通过 FormData 上传文件
  1. /**
  2. * 上传文件
  3. * @param {string} url 上传文件的接口地址
  4. * @param {Object} files 文件数据对象(支持多个文件)
  5. * @param {Object} [extraData] 额外的表单数据
  6. * @param {Object} [headers={}] 自定义请求头
  7. * @returns {Promise} 返回一个 Promise
  8. */
  9. const uploadFile = (url, files, extraData = {}, headers = {}) => {
  10.   const formData = new FormData();
  11.   // 将文件和额外数据添加到 FormData
  12.   Object.keys(files).forEach((key) => {
  13.     formData.append(key, files[key]);
  14.   });
  15.   Object.keys(extraData).forEach((key) => {
  16.     formData.append(key, extraData[key]);
  17.   });
  18.   return axiosInstance.post(url, formData, {
  19.     headers: {
  20.       'Content-Type': 'multipart/form-data',
  21.       ...headers,
  22.     },
  23.   });
  24. };
复制代码
4. 通用请求方法封装

支持动态参数、错误处理、失败重试等功能:
  1. /**
  2. * 通用请求方法封装
  3. * @param {Object} options 请求配置选项
  4. * @param {string} options.url 请求地址
  5. * @param {string} [options.method='GET'] 请求方法
  6. * @param {Object} [options.params={}] 查询字符串参数
  7. * @param {Object} [options.data={}] 请求体数据
  8. * @param {Object} [options.headers={}] 自定义请求头
  9. * @param {boolean} [options.showLoading=false] 是否显示全局 Loading
  10. * @param {number} [options.retries=0] 请求失败时的重试次数
  11. * @param {number} [options.retryDelay=1000] 重试间隔时间(毫秒)
  12. * @param {number} [options.timeout=10000] 请求超时时间(毫秒)
  13. * @returns {Promise} 返回一个 Promise
  14. */
  15. const request = async ({
  16.   url,
  17.   method = 'GET',
  18.   params = {},
  19.   data = {},
  20.   headers = {},
  21.   showLoading = false,
  22.   retries = 0,
  23.   retryDelay = 1000,
  24.   timeout = 10000,
  25. }) => {
  26.   const config = { url, method, params, data, headers, timeout };
  27.   const sendRequest = async (retryCount) => {
  28.     try {
  29.       removePendingRequest(config); // 防止重复请求
  30.       addPendingRequest(config); // 添加当前请求
  31.       const response = await axiosInstance(config);
  32.       return response.data;
  33.     } catch (error) {
  34.       if (retryCount > 0) {
  35.         await new Promise((resolve) => setTimeout(resolve, retryDelay));
  36.         return sendRequest(retryCount - 1); // 递归重试
  37.       }
  38.       throw error;
  39.     } finally {
  40.       removePendingRequest(config); // 请求完成后移除
  41.     }
  42.   };
  43.   if (showLoading) showGlobalLoading(); // 显示 Loading
  44.   try {
  45.     return await sendRequest(retries);
  46.   } finally {
  47.     if (showLoading) hideGlobalLoading(); // 隐藏 Loading
  48.   }
  49. };
  50. export { request, uploadFile, clearPendingRequests };
复制代码
5. 请求拦截器

在请求发送前,添加拦截器来实现以下功能:

  1. axiosInstance.interceptors.request.use(
  2.   (config) => {
  3.     // 动态添加 Token
  4.     const token = localStorage.getItem('token');
  5.     if (token) {
  6.       config.headers['Authorization'] = `Bearer ${token}`;
  7.     }
  8.     // 开启全局 Loading
  9.     if (config.showLoading) {
  10.       showGlobalLoading(); // 假设有全局 Loading 的控制函数
  11.     }
  12.     return config;
  13.   },
  14.   (error) => {
  15.     // 请求错误处理
  16.     return Promise.reject(error);
  17.   }
  18. );
复制代码
6. 相应拦截器

在相应阶段,统一处理以下环境:

  1. axiosInstance.interceptors.response.use(
  2.   (response) => {
  3.     // 隐藏全局 Loading
  4.     hideGlobalLoading(); // 假设有全局 Loading 的控制函数
  5.     // 根据业务状态码处理逻辑
  6.     const { code, message, data } = response.data;
  7.     if (code === 200) {
  8.       // 请求成功
  9.       return data;
  10.     } else {
  11.       // 业务错误
  12.       console.error(`业务错误:${message}`);
  13.       return Promise.reject(new Error(message));
  14.     }
  15.   },
  16.   (error) => {
  17.     // 隐藏全局 Loading
  18.     hideGlobalLoading();
  19.     // 统一处理 HTTP 错误状态码
  20.     if (error.response) {
  21.       const { status } = error.response;
  22.       switch (status) {
  23.         case 401:
  24.           console.error('未登录,请重新登录');
  25.           // 跳转到登录页面
  26.           break;
  27.         case 403:
  28.           console.error('无权限访问');
  29.           break;
  30.         case 500:
  31.           console.error('服务器错误,请稍后重试');
  32.           break;
  33.         default:
  34.           console.error('请求失败');
  35.       }
  36.     }
  37.     return Promise.reject(error);
  38.   }
  39. );
复制代码
利用示例

1. 发起 GET 请求

  1. import { get } from './request';
  2. get('/api/users', { page: 1, size: 10 }, true)
  3.   .then((data) => console.log('成功:', data))
  4.   .catch((error) => console.error('失败:', error));
复制代码
2. 发起 POST 请求

  1. import { post } from './request';
  2. post('/api/login', { username: 'admin', password: '123456' }, true)
  3.   .then((data) => console.log('登录成功:', data))
  4.   .catch((error) => console.error('登录失败:', error));
复制代码

总结

通过对 Axios 请求模块的封装,我们成功实现了一个功能全面、机动易用的通用请求工具。该工具不但满意了常见的开发需求,还具备了高扩展性和可维护性。其核心功能包括:
在现实开发中,这个封装模块可以极大地提拔代码复用性和团队协作服从,同时淘汰手动处理请求的复杂度。如果有更多个性化需求,也可以在此底子上进一步扩展,如支持更多的请求级功能(例如请求优先级、队列管理等)。
通过这样的封装,我们不但办理了开发中的常见痛点,也为后续的功能扩展提供了强盛的底子。希望这份封装工具能够帮助您更高效地完成项目开发!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4