立山 发表于 2025-3-15 18:33:09

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 安装

npm install axios 2. 利用 yarn 安装

yarn add axios 封装目标

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

[*]支持多种请求方法:如 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} 额外的表单数据
* @param {Object} 自定义请求头
* @returns {Promise} 返回一个 Promise
*/
const uploadFile = (url, files, extraData = {}, headers = {}) => {
const formData = new FormData();

// 将文件和额外数据添加到 FormData
Object.keys(files).forEach((key) => {
    formData.append(key, files);
});

Object.keys(extraData).forEach((key) => {
    formData.append(key, extraData);
});

return axiosInstance.post(url, formData, {
    headers: {
      'Content-Type': 'multipart/form-data',
      ...headers,
    },
});
}; 4. 通用请求方法封装

支持动态参数、错误处理、失败重试等功能:
/**
* 通用请求方法封装
* @param {Object} options 请求配置选项
* @param {string} options.url 请求地址
* @param {string} 请求方法
* @param {Object} 查询字符串参数
* @param {Object} 请求体数据
* @param {Object} 自定义请求头
* @param {boolean} 是否显示全局 Loading
* @param {number} 请求失败时的重试次数
* @param {number} 重试间隔时间(毫秒)
* @param {number} 请求超时时间(毫秒)
* @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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Axios 实战:拦截器、封装与工作中的关键应用