Axios 指南

91 阅读12分钟

Axios 指南:现代前端最常用的 HTTP 请求库

前言

在现代前端开发中,发送 HTTP 请求几乎是最基础、最常见的能力之一。无论是登录注册、获取用户列表、提交表单,还是上传文件、调用第三方接口,本质上都离不开网络请求。

在 JavaScript 中,我们既可以使用原生的 fetch,也可以使用第三方请求库。而在实际开发中,Axios 一直是非常受欢迎的选择。

Axios 是一个基于 Promise 的 HTTP 客户端,它语法简洁、功能完整,支持:

  • 请求和响应拦截器
  • 自动处理 JSON
  • 超时控制
  • 统一错误处理
  • 请求取消
  • 上传下载进度监听
  • 浏览器与 Node.js 双端支持

本文将从 安装、基本用法、请求配置、错误处理、拦截器、Axios 实例、取消请求、项目封装 等方面,系统讲解 Axios 的使用方式,帮助你真正掌握这个常用工具。


一、什么是 Axios?

1.1 Axios 简介

Axios 是一个基于 Promise 的 HTTP 请求库,可以运行在:

  • 浏览器环境
  • Node.js 环境

它是一个同构(isomorphic)的请求库,意味着同一套 API 可以同时适用于前端和服务端。

在不同环境下,Axios 底层实现不同:

  • 在浏览器中,基于 XMLHttpRequest
  • 在 Node.js 中,基于原生 http/https 模块

这也是它能够跨环境使用的重要原因。


1.2 为什么很多项目选择 Axios?

相比原生 XMLHttpRequestfetch,Axios 有几个明显优势:

1)基于 Promise

Axios 的 API 天然支持 Promise,因此可以非常方便地配合:

  • .then() / .catch()
  • async / await

2)自动处理 JSON

Axios 会自动对常见 JSON 数据进行处理:

  • 请求时可直接传对象
  • 响应时常见 JSON 会自动解析

这让代码更简洁。

3)支持请求/响应拦截器

这点在实际项目中非常重要。
常见用途包括:

  • 请求时自动带上 token
  • 响应后统一处理错误
  • 统一处理登录失效
  • 全局 loading 管理

4)更友好的错误处理

Axios 会根据 HTTP 状态码自动区分成功和失败响应,错误对象结构也比较清晰,便于统一封装。

5)支持超时设置

原生 fetch 不内置超时控制,而 Axios 可以通过 timeout 配置请求超时。

6)支持取消请求

可以使用 AbortController 取消正在进行的请求,适合搜索联想、组件卸载场景。

7)支持上传和下载进度监听

尤其适合文件上传、文件下载等功能。


1.3 Axios 和 fetch 有什么区别?

很多人会问:既然浏览器已经有 fetch 了,为什么还要用 Axios?

简单对比如下:

对比项Axiosfetch
是否基于 Promise
自动 JSON 转换更方便需要手动 response.json()
超时支持内置 timeout需自己配合 AbortController
请求拦截器支持原生不支持
响应拦截器支持原生不支持
取消请求支持支持
错误处理更适合业务封装需要自己额外处理

如果是学习原理,可以多练 fetch
如果是实际项目开发,Axios 往往更高效。


二、安装与引入

2.1 安装 Axios

使用 npm:

npm install axios

使用 yarn:

yarn add axios

使用 pnpm:

pnpm add axios

2.2 引入方式

ES Module 方式

import axios from 'axios';

CommonJS 方式

const axios = require('axios');

CDN 方式

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  axios.get('/api/users').then(res => {
    console.log(res.data);
  });
</script>

在现代前端项目中,通常推荐直接通过包管理器安装,并使用 import 引入。


三、Axios 的基本用法

3.1 GET 请求

最基础的 GET 请求写法如下:

import axios from 'axios';

axios.get('/api/users')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });

带查询参数的 GET 请求

axios.get('/api/users', {
  params: {
    page: 1,
    limit: 10,
    keyword: '张三'
  }
}).then(response => {
  console.log(response.data);
});

Axios 会自动帮你把 params 拼接到 URL 上,例如:

/api/users?page=1&limit=10&keyword=张三

使用 async/await

async function fetchUsers() {
  try {
    const response = await axios.get('/api/users');
    console.log(response.data);
    return response.data;
  } catch (error) {
    console.error('获取用户失败:', error);
    throw error;
  }
}

在实际项目里,async/await 往往比 .then() 更清晰。


3.2 POST 请求

POST 通常用于新增数据、提交表单等。

axios.post('/api/users', {
  name: '张三',
  email: 'zhangsan@example.com',
  age: 25
})
  .then(response => {
    console.log('创建成功:', response.data);
  })
  .catch(error => {
    console.error('创建失败:', error);
  });

封装成函数

async function createUser(userData) {
  try {
    const response = await axios.post('/api/users', userData);
    return response.data;
  } catch (error) {
    console.error('创建用户失败:', error);
    throw error;
  }
}

发送 FormData

const formData = new FormData();
formData.append('name', '张三');
formData.append('avatar', fileInput.files[0]);

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

通常上传文件时会使用 FormData


3.3 PUT 和 PATCH 请求

虽然开发中最常见的是 GET 和 POST,但 PUT、PATCH 也很常见。

PUT:完整更新

axios.put('/api/users/1', {
  name: '李四',
  email: 'lisi@example.com',
  age: 30
}).then(response => {
  console.log('更新成功:', response.data);
});

PATCH:部分更新

axios.patch('/api/users/1', {
  age: 31
}).then(response => {
  console.log('部分更新成功:', response.data);
});

一般可以这样理解:

  • PUT:整体替换
  • PATCH:局部修改

3.4 DELETE 请求

axios.delete('/api/users/1')
  .then(response => {
    console.log('删除成功:', response.data);
  })
  .catch(error => {
    console.error('删除失败:', error);
  });

3.5 通用写法

Axios 还支持统一配置对象写法:

axios({
  url: '/api/users',
  method: 'post',
  data: {
    name: '张三',
    age: 20
  }
}).then(response => {
  console.log(response.data);
});

这种写法在封装通用请求函数时很常见。


四、并发请求

在实际开发中,经常会遇到需要同时请求多个接口的场景,比如:

  • 页面初始化时同时获取用户信息、菜单、通知列表
  • 同时请求多个模块的数据

最推荐的方式是使用 Promise.all()

async function fetchAllData() {
  try {
    const [usersRes, postsRes, commentsRes] = await Promise.all([
      axios.get('/api/users'),
      axios.get('/api/posts'),
      axios.get('/api/comments')
    ]);

    return {
      users: usersRes.data,
      posts: postsRes.data,
      comments: commentsRes.data
    };
  } catch (error) {
    console.error('获取数据失败:', error);
    throw error;
  }
}

说明

  • 全部成功才会进入 then
  • 只要一个失败,就会进入 catch

现代项目中更推荐直接使用 Promise.all(),而不是 axios.all() / axios.spread()


五、请求配置详解

Axios 的强大之处,很大一部分来自灵活的请求配置。

5.1 基本配置示例

axios({
  method: 'post',
  url: '/api/users',
  baseURL: 'https://api.example.com',
  data: {
    name: '张三',
    email: 'zhangsan@example.com'
  },
  params: {
    id: 123
  },
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer token123'
  },
  timeout: 5000
});

5.2 常用配置项

{
  url: '/api/users',              // 请求地址
  method: 'get',                  // 请求方法
  baseURL: 'https://api.example.com', // 基础地址
  headers: {},                    // 请求头
  params: { page: 1 },            // URL 查询参数
  data: { name: 'Tom' },          // 请求体数据
  timeout: 5000,                  // 超时时间
  responseType: 'json',           // 响应类型
  withCredentials: false,         // 是否携带 cookie
  signal: controller.signal,      // 取消请求信号
  onUploadProgress(progressEvent) {
    console.log(progressEvent);
  },
  onDownloadProgress(progressEvent) {
    console.log(progressEvent);
  }
}

5.3 params 和 data 的区别

这两个是初学者最容易混淆的点。

params

一般用于 GET 请求的查询参数,会拼接到 URL 上:

axios.get('/api/users', {
  params: {
    page: 1,
    limit: 10
  }
});

生成效果类似:

/api/users?page=1&limit=10

data

一般用于 POST、PUT、PATCH 请求体:

axios.post('/api/users', {
  name: '张三',
  age: 18
});

六、响应结构

当请求成功后,Axios 返回的是一个响应对象,而真正的业务数据通常在 response.data 中。

axios.get('/api/users')
  .then(response => {
    console.log(response.data);       // 响应数据
    console.log(response.status);     // HTTP 状态码
    console.log(response.statusText); // 状态文本
    console.log(response.headers);    // 响应头
    console.log(response.config);     // 本次请求配置
  });

常见结构如下:

{
  data: {},        
  status: 200,
  statusText: 'OK',
  headers: {},
  config: {},
  request: {}
}

实际开发中最常用的是:

  • response.data
  • response.status

七、错误处理

Axios 的错误处理是非常重要的一部分,也是它比原生请求更适合项目封装的原因之一。

7.1 基本错误处理

axios.get('/api/users')
  .catch(error => {
    if (error.response) {
      // 服务器响应了,但状态码不是 2xx
      console.error('状态码:', error.response.status);
      console.error('响应数据:', error.response.data);
    } else if (error.request) {
      // 请求发出去了,但没有收到响应
      console.error('未收到响应:', error.request);
    } else {
      // 请求配置出错
      console.error('请求错误:', error.message);
    }
  });

7.2 Axios 错误对象怎么理解?

一般可以从这三个角度判断:

1)error.response

说明请求已经到达服务器,服务器也返回了响应,只不过是错误状态码,比如:

  • 400
  • 401
  • 403
  • 404
  • 500

2)error.request

说明请求已经发出,但没有收到响应,可能是:

  • 网络断开
  • 请求超时
  • 服务不可达

3)其他情况

比如:

  • 请求配置错误
  • 代码拼写问题
  • 参数异常

7.3 更实用的错误处理方式

async function fetchData(url) {
  try {
    const response = await axios.get(url);
    return response.data;
  } catch (error) {
    if (error.response) {
      const { status, statusText, data } = error.response;

      switch (status) {
        case 400:
          console.error('请求参数错误');
          break;
        case 401:
          console.error('未授权,请重新登录');
          break;
        case 403:
          console.error('无权限访问');
          break;
        case 404:
          console.error('资源不存在');
          break;
        case 500:
          console.error('服务器内部错误');
          break;
        default:
          console.error(`请求失败 [${status}] ${statusText}`);
      }

      throw {
        status,
        message: data?.message || statusText,
        data
      };
    } else if (error.request) {
      throw {
        message: '网络错误,请检查网络连接'
      };
    } else {
      throw {
        message: error.message
      };
    }
  }
}

八、拦截器

拦截器是 Axios 最常用、最重要的功能之一。
你可以把它理解为:在请求发出前和响应返回后,先统一做一层处理。


8.1 请求拦截器

请求拦截器常用于:

  • 自动携带 token
  • 统一设置请求头
  • 添加时间戳防缓存
  • 显示 loading
axios.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token');

    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    if (config.method === 'get') {
      config.params = config.params || {};
      config.params._t = Date.now();
    }

    console.log('发送请求:', config.url);

    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

8.2 响应拦截器

响应拦截器常用于:

  • 统一处理接口返回格式
  • 统一弹出错误提示
  • token 失效时跳转登录
  • 返回真正的业务数据,简化调用代码
axios.interceptors.response.use(
  response => {
    const res = response.data;

    // 假设后端统一返回 { code, data, message }
    if (res.code === 200) {
      return res.data;
    }

    return Promise.reject(new Error(res.message || '请求失败'));
  },
  error => {
    if (error.response) {
      const status = error.response.status;

      if (status === 401) {
        localStorage.removeItem('token');
        window.location.href = '/login';
      } else if (status === 403) {
        console.error('没有权限访问此资源');
      } else if (status >= 500) {
        console.error('服务器错误,请稍后重试');
      }
    }

    return Promise.reject(error);
  }
);

8.3 移除拦截器

如果你需要取消某个拦截器,可以使用 eject

const interceptorId = axios.interceptors.request.use(config => config);

axios.interceptors.request.eject(interceptorId);

九、Axios 实例

在实际项目中,不建议直接在所有地方都写全局 axios.get()
更推荐使用 Axios 实例

9.1 为什么要创建实例?

因为不同项目、不同模块通常有不同配置,例如:

  • 不同的 baseURL
  • 不同的超时时间
  • 不同的请求头
  • 不同的拦截器逻辑

所以更合理的做法是创建自己的请求实例。


9.2 创建实例

import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

apiClient.get('/users').then(res => {
  console.log(res.data);
});

9.3 给实例添加拦截器

apiClient.interceptors.request.use(config => {
  const token = localStorage.getItem('token');

  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});

9.4 多实例场景

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000
});

const uploadClient = axios.create({
  baseURL: 'https://upload.example.com',
  timeout: 30000
});

const thirdPartyClient = axios.create({
  baseURL: 'https://third-party.example.com',
  timeout: 5000
});

适用于:

  • 主业务接口
  • 文件上传接口
  • 第三方平台接口

十、取消请求

在搜索联想、路由切换、组件卸载等场景中,经常需要取消未完成的请求。

目前推荐使用 AbortController

10.1 基本用法

const controller = new AbortController();

axios.get('/api/users', {
  signal: controller.signal
})
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    if (error.code === 'ERR_CANCELED') {
      console.log('请求已取消');
    } else {
      console.error('请求失败:', error);
    }
  });

// 取消请求
controller.abort();

10.2 在组件中使用的典型场景

例如在 Vue 组件中:

export default {
  data() {
    return {
      controller: null
    };
  },
  methods: {
    async fetchData() {
      if (this.controller) {
        this.controller.abort();
      }

      this.controller = new AbortController();

      try {
        const response = await axios.get('/api/users', {
          signal: this.controller.signal
        });
        return response.data;
      } catch (error) {
        if (error.code === 'ERR_CANCELED') {
          console.log('请求已取消');
        } else {
          console.error('请求失败:', error);
        }
      }
    }
  },
  beforeUnmount() {
    if (this.controller) {
      this.controller.abort();
    }
  }
};

这样可以避免:

  • 重复请求
  • 旧请求结果覆盖新请求结果
  • 组件销毁后仍然更新状态

十一、默认配置

Axios 支持全局默认配置和实例默认配置。

11.1 全局默认配置

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.timeout = 5000;
axios.defaults.headers.common['Authorization'] = 'Bearer token';
axios.defaults.headers.post['Content-Type'] = 'application/json';

不过在真实项目里,更推荐使用实例默认配置,因为全局配置过多会让维护变复杂。


11.2 实例默认配置

const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

instance.defaults.headers.common['Authorization'] = 'Bearer new-token';

11.3 配置优先级

配置优先级从高到低:

  1. 请求级配置
  2. 实例默认配置
  3. 全局默认配置

例如:

instance.get('/users', {
  timeout: 2000
});

这里的 timeout: 2000 会覆盖实例和全局的默认配置。


十二、项目中如何封装 Axios

这一部分是实际开发里最有价值的内容之一。

目标通常是做到:

  • 所有请求都走统一入口
  • 自动处理 token
  • 自动处理错误
  • 调用接口时只关心业务本身

12.1 封装一个 request 工具

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

// 创建实例
const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token');

    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    return config;
  },
  error => Promise.reject(error)
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data;

    // 假设后端返回格式:{ code, data, message }
    if (res.code === 200) {
      return res.data;
    }

    return Promise.reject(new Error(res.message || '请求失败'));
  },
  error => {
    if (error.response) {
      const { status, data } = error.response;

      if (status === 401) {
        localStorage.removeItem('token');
        window.location.href = '/login';
      }

      const msg = data?.message || `请求失败 [${status}]`;
      return Promise.reject(new Error(msg));
    }

    if (error.code === 'ERR_CANCELED') {
      return Promise.reject(error);
    }

    return Promise.reject(new Error('网络错误,请稍后重试'));
  }
);

export default service;

12.2 接口模块化封装

// api/user.js
import request from '@/utils/request';

// 获取用户列表
export function getUserList(params) {
  return request({
    url: '/users',
    method: 'get',
    params
  });
}

// 获取用户详情
export function getUserDetail(id) {
  return request({
    url: `/users/${id}`,
    method: 'get'
  });
}

// 创建用户
export function createUser(data) {
  return request({
    url: '/users',
    method: 'post',
    data
  });
}

// 更新用户
export function updateUser(id, data) {
  return request({
    url: `/users/${id}`,
    method: 'put',
    data
  });
}

// 删除用户
export function deleteUser(id) {
  return request({
    url: `/users/${id}`,
    method: 'delete'
  });
}

这样做的好处是:

  • 调用时更简洁
  • 业务层不需要重复写 URL 和 method
  • 接口集中管理,更容易维护

12.3 在 Vue 组件中使用

import { getUserList, createUser } from '@/api/user';

export default {
  data() {
    return {
      users: [],
      loading: false
    };
  },
  async mounted() {
    await this.loadUsers();
  },
  methods: {
    async loadUsers() {
      this.loading = true;
      try {
        this.users = await getUserList({
          page: 1,
          limit: 10
        });
      } catch (error) {
        console.error('加载用户失败:', error.message);
      } finally {
        this.loading = false;
      }
    },

    async handleCreateUser(userData) {
      try {
        await createUser(userData);
        this.$message.success('创建成功');
        await this.loadUsers();
      } catch (error) {
        console.error('创建用户失败:', error.message);
      }
    }
  }
};

十三、文件上传示例

文件上传是 Axios 很常见的使用场景。

13.1 上传单个文件

async function uploadFile(file) {
  const formData = new FormData();
  formData.append('file', file);

  try {
    const response = await axios.post('/api/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: progressEvent => {
        const total = progressEvent.total || 1;
        const percentCompleted = Math.round((progressEvent.loaded * 100) / total);
        console.log('上传进度:' + percentCompleted + '%');
      }
    });

    return response.data;
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

13.2 上传多个文件

async function uploadFiles(files) {
  const formData = new FormData();

  files.forEach(file => {
    formData.append('files', file);
  });

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

十四、常见问题与最佳实践

14.1 如何处理跨域问题?

跨域问题本质上是浏览器安全策略导致的,真正解决方式通常在后端。

前端常见做法:

1)开发环境代理转发

例如在 Vite 中配置代理:

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
};

2)需要携带 cookie 时配置 withCredentials

axios.get('/api/user', {
  withCredentials: true
});

同时后端也必须正确设置 CORS 响应头。


14.2 如何防止重复请求?

例如用户连续点击按钮、搜索框快速输入时,可能触发重复请求。
常见方案有:

  • 取消上一次请求
  • 给请求做唯一标识
  • 请求中时禁用按钮
  • 对输入进行防抖/节流

例如搜索场景中更推荐使用 AbortController 取消旧请求。


14.3 如何实现请求重试?

async function requestWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }

      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

适用于:

  • 网络偶发波动
  • 非核心接口可做简单重试

但要注意:

  • 不要对所有请求盲目重试
  • 提交类请求要特别小心,避免重复提交

14.4 实际项目中的最佳实践

1)统一封装 Axios 实例

不要把 axios.get() 散落在所有页面和组件里。

2)统一处理 token

通过请求拦截器自动带上身份信息。

3)统一处理错误

通过响应拦截器处理:

  • 登录失效
  • 权限不足
  • 服务端错误
  • 网络错误

4)接口模块化

按业务模块拆分接口文件,例如:

  • api/user.js
  • api/order.js
  • api/product.js

5)优先使用 async/await

代码通常更直观。

6)组件卸载时取消请求

防止内存泄漏或错误更新状态。

7)谨慎使用全局默认配置

优先使用实例配置,避免污染全局。

8)配合 TypeScript 使用更佳

如果项目使用 TS,可以为请求参数和返回结果定义类型,提高可维护性。


十五、Axios 常用语法速查

GET 请求

axios.get('/api/users', {
  params: { page: 1 }
});

POST 请求

axios.post('/api/users', {
  name: 'Tom'
});

PUT 请求

axios.put('/api/users/1', {
  name: 'Tom'
});

PATCH 请求

axios.patch('/api/users/1', {
  age: 20
});

DELETE 请求

axios.delete('/api/users/1');

创建实例

const request = axios.create({
  baseURL: '/api',
  timeout: 10000
});

请求拦截器

request.interceptors.request.use(config => config);

响应拦截器

request.interceptors.response.use(response => response.data);

取消请求

const controller = new AbortController();

axios.get('/api/users', {
  signal: controller.signal
});

controller.abort();

十六、总结

Axios 是现代前端开发中非常实用的一款 HTTP 请求库。它不仅能完成基础的 GET、POST、PUT、DELETE 请求,还提供了很多适合工程化开发的能力,例如:

  • 基于 Promise,支持 async/await
  • 请求与响应拦截器
  • 更清晰的错误处理
  • 实例化配置
  • 请求取消
  • 上传下载进度监听
  • 浏览器与 Node.js 双端支持

如果你只是做简单练习,原生 fetch 已经够用;
但如果你要开发真实项目,尤其是中大型前端项目,Axios 往往能显著提升开发效率和代码可维护性。

建议你真正掌握下面这几部分:

  1. 基本请求写法
  2. paramsdata 的区别
  3. 响应结构和错误处理
  4. 请求/响应拦截器
  5. Axios 实例封装
  6. 请求取消和项目化实践

只要这几部分吃透,Axios 基本就算掌握了。