Skip to content

Axios 网络请求库与工程化封装指南

本文全面、系统地介绍现代前端主流 HTTP 库 Axios 的核心概念、请求配置参数、参数发送格式、拦截器工作原理、高级取消请求与生产级工程化单例封装。

1. Axios 概述与核心特性

Axios 是一个基于 Promise 的 HTTP 客户端,同时支持在浏览器Node.js 环境中运行(同构网络库)。

核心特性

  1. 同构跨平台:在浏览器端底层基于 XMLHttpRequest 发送请求;在 Node.js 端底层基于原生 http / https 模块。
  2. 自动转换 JSON 数据:自动将响应的 JSON 字符串转为 JavaScript 对象,向服务端发送 POST 请求时自动将 JS 对象序列化为 JSON 字符串。
  3. 强大的拦截器体系:支持全局注册 请求拦截器 (Request Interceptors)响应拦截器 (Response Interceptors)
  4. 防止 CSRF:支持设置客户端自动携带跨站请求伪造 Token。
  5. 取消请求机制:基于原生的 AbortController 优雅取消未完成的网络请求。

2. Axios 基础 API 与请求方式

2.1 常用请求别名方法

javascript
// 1. GET 请求:带 URL 查询参数 (?page=1&pageSize=10)
axios.get('/api/users', {
    params: { page: 1, pageSize: 10 }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));

// 2. POST 请求:提交 JSON 格式数据
axios.post('/api/users', {
    username: 'Alice',
    email: 'alice@example.com'
})
.then(response => console.log(response.data));

// 3. PUT 请求:更新数据
axios.put('/api/users/101', { username: 'AliceUpdated' });

// 4. DELETE 请求:删除数据
axios.delete('/api/users/101');

3. 请求参数发送格式 (Content-Type)

不同后端接口对请求数据的格式要求有所不同,Axios 提供了对多种格式的支持:

3.1 JSON 格式(最常用,application/json

直接将 JS 对象传给 data,Axios 默认将 Header 设置为 application/json

javascript
axios.post('/api/data', { id: 1, name: 'Test' });

3.2 Form 表单格式 (application/x-www-form-urlencoded)

可以使用 URLSearchParams 进行传参:

javascript
const params = new URLSearchParams();
params.append('username', 'admin');
params.append('password', '123456');

axios.post('/api/login', params);

3.3 文件上传 (multipart/form-data)

上传图片或文件时,需要使用原生 FormData 对象:

javascript
const formData = new FormData();
formData.append('avatar', fileInput.files[0]); // 文件对象
formData.append('userId', '1001');

axios.post('/api/upload', formData, {
    headers: {
        'Content-Type': 'multipart/form-data'
    }
});

4. 拦截器 (Interceptors) 原理与链式调用

拦截器是 Axios 最强大的功能之一。请求发起时先经过请求拦截器,接收响应时先经过响应拦截器

[ 发起请求 ] ──> [ 请求拦截器 (注入Token/加签) ] ──> [ 发送到服务器 ]

[ 返回页面 ] <── [ 响应拦截器 (解包/统一错误提示) ] <─── [ 收到服务器响应 ]
javascript
// 添加请求拦截器
axios.interceptors.request.use(config => {
    // 在发送请求之前做些什么
    config.headers.Authorization = 'Bearer token_abc123';
    return config;
}, error => {
    return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(response => {
    // 对响应数据做点什么
    return response.data; // 直接解包返回 data
}, error => {
    // 对响应错误做点什么
    return Promise.reject(error);
});

5. 生产级 Axios 工程化单例封装实践

在企业级前端项目(Vue3 / React)中,我们通常建立一个模块化的 HTTP 工具包:

javascript
// src/utils/request.js
import axios from 'axios';

// 1. 创建 Axios 单例
const request = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 环境变量配置接口地址
    timeout: 10000, // 请求超时时间 (10秒)
    headers: {
        'Content-Type': 'application/json;charset=UTF-8'
    }
});

// 2. 请求拦截器:自动注入 Authorization Token
request.interceptors.request.use(
    (config) => {
        const token = localStorage.getItem('ACCESS_TOKEN');
        if (token) {
            config.headers['Authorization'] = `Bearer ${token}`;
        }
        return config;
    },
    (error) => Promise.reject(error)
);

// 3. 响应拦截器:统一解包与全局错误提示
request.interceptors.response.use(
    (response) => {
        const { code, message, data } = response.data;
        // 假设服务端约定的成功业务码 code === 200
        if (code === 200) {
            return data;
        }
        
        // 处理特殊业务错误码(如 401 登录已过期)
        if (code === 401) {
            localStorage.removeItem('ACCESS_TOKEN');
            window.location.href = '/login';
        }
        
        console.error(`业务异常 [${code}]: ${message}`);
        return Promise.reject(new Error(message || 'Error'));
    },
    (error) => {
        // 统一处理 HTTP 状态码异常
        let errorMessage = '网络繁忙,请稍后再试';
        if (error.response) {
            const status = error.response.status;
            switch (status) {
                case 400: errorMessage = '请求参数错误 (400)'; break;
                case 403: errorMessage = '拒绝访问,无权限 (403)'; break;
                case 404: errorMessage = '请求的资源不存在 (404)'; break;
                case 500: errorMessage = '服务器内部致命错误 (500)'; break;
            }
        } else if (error.message.includes('timeout')) {
            errorMessage = '网络请求超时,请检查网络连接';
        }
        alert(errorMessage);
        return Promise.reject(error);
    }
);

export default request;

6. 取消请求与防止重复提交

在搜索框联想或频繁切换 Tab 页时,旧的未完成请求可能会覆盖新请求的数据。使用原生的 AbortController 可以取消未完成的请求:

javascript
let controller = null;

function fetchSearchResults(keyword) {
    // 1. 如果已有正在进行的请求,立即取消它
    if (controller) {
        controller.abort();
    }

    // 2. 新建 AbortController 实例
    controller = new AbortController();

    // 3. 将 signal 信号传入请求配置中
    axios.get('/api/search', {
        params: { q: keyword },
        signal: controller.signal
    })
    .then(response => {
        console.log('搜索结果:', response.data);
    })
    .catch(error => {
        if (axios.isCancel(error)) {
            console.log('上一请求已被成功取消');
        } else {
            console.error('请求出错:', error);
        }
    });
}

基于 VitePress 构建 | 技术知识库