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?
相比原生 XMLHttpRequest 和 fetch,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?
简单对比如下:
| 对比项 | Axios | fetch |
|---|---|---|
| 是否基于 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.dataresponse.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 配置优先级
配置优先级从高到低:
- 请求级配置
- 实例默认配置
- 全局默认配置
例如:
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.jsapi/order.jsapi/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 往往能显著提升开发效率和代码可维护性。
建议你真正掌握下面这几部分:
- 基本请求写法
params和data的区别- 响应结构和错误处理
- 请求/响应拦截器
- Axios 实例封装
- 请求取消和项目化实践
只要这几部分吃透,Axios 基本就算掌握了。