Axios 是目前最流行的 JavaScript HTTP 客户端之一,它基于 Promise 封装,专为简化和优化浏览器和 Node.js 环境下的 HTTP 请求操作。尽管 Axios 功能强大且易用,实际项目中封装它的功能以满足更复杂的需求仍是常见的开发任务。本文将深入探讨如何对 Axios 进行封装,以提高代码的可维护性、复用性和扩展性。
Axios 简介
Axios 是一个基于 Promise 的 HTTP 客户端,支持跨浏览器请求、自动 JSON 数据转换、拦截器、自定义配置、请求和响应的数据转换等功能。与原生 fetch API 相比,Axios 的特点在于:
- 自动将请求和响应的数据序列化/反序列化为 JSON。
- 支持
interceptors(拦截器),用于在请求或响应被处理之前对其进行修改。 - 允许在浏览器环境中进行跨域请求处理。
- 支持
cancel(请求取消)、并发请求和超时设置。
为什么要封装 Axios?
尽管 Axios 本身功能强大且灵活,但在实际开发中,我们通常需要进一步封装 Axios 以满足业务需求。原因如下:
- 统一错误处理:每次请求都需要进行错误捕获和处理,封装后可以简化重复代码。
- 添加全局拦截器:如在所有请求中自动附加认证 token,或在请求前后统一处理 loading 状态。
- 多环境支持:通过封装,方便处理不同环境(开发、测试、生产)下的接口配置。
- 重试机制:当请求失败时,可能需要自动重试机制。
- 取消请求和并发控制:针对一些特殊场景,我们需要对多次请求进行控制,或取消一些不必要的请求。
Axios 的基础封装
在进行封装时,我们可以利用 Axios 提供的配置选项来创建一个具有统一配置的实例。首先,我们可以创建一个单独的 Axios 实例,并设置基础的 baseURL 和 timeout 等全局配置。
import axios from 'axios';
// 创建 Axios 实例
const axiosInstance = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 基础 URL
timeout: 10000, // 请求超时时间
headers: {
'Content-Type': 'application/json',
},
});
// 导出实例,供其他模块使用
export default axiosInstance;
在这个基础封装中,我们使用 axios.create() 方法创建了一个自定义的 Axios 实例,统一设置了基础 URL 和请求超时时间。这样,可以避免每次请求时重复编写这些配置,简化了代码。
请求拦截与响应拦截的封装
Axios 提供了 interceptors(拦截器)功能,它允许我们在请求或响应被处理之前对其进行修改。拦截器常用于添加全局的逻辑,例如:为每个请求添加认证 token,或者在响应返回前处理一些全局的错误处理逻辑。
请求拦截器
请求拦截器可以在请求发送之前修改请求。例如,给请求头添加 Authorization token。
// 添加请求拦截器
axiosInstance.interceptors.request.use(
config => {
// 在请求发送之前做些什么,例如添加 token
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
响应拦截器
响应拦截器可以在响应到达前统一处理错误信息,或者对返回的数据进行预处理。
// 添加响应拦截器
axiosInstance.interceptors.response.use(
response => {
// 对响应数据做点什么,例如返回实际数据
return response.data;
},
error => {
// 对响应错误做点什么,例如统一处理错误
if (error.response) {
switch (error.response.status) {
case 401:
console.error('未授权,请重新登录');
break;
case 500:
console.error('服务器错误,请稍后再试');
break;
default:
console.error(error.response.data.message);
}
}
return Promise.reject(error);
}
);
通过这种方式,可以避免在每次请求和响应中重复处理错误或认证逻辑,提升代码的复用性和可维护性。
自动重试机制的实现
在网络请求中,某些操作可能由于临时的网络波动或其他不可控因素而失败。在这种情况下,自动重试机制可以增加请求的成功率。我们可以通过 Axios 的拦截器结合递归函数实现自动重试。
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000,
});
axiosInstance.interceptors.response.use(
response => response,
error => {
const config = error.config;
if (!config || !config.retry) return Promise.reject(error);
// 设置重试次数和重试间隔
config.__retryCount = config.__retryCount || 0;
if (config.__retryCount >= config.retry) {
return Promise.reject(error);
}
config.__retryCount += 1;
// 创建新的 Promise 来处理延迟重试
const backoff = new Promise(resolve => {
setTimeout(() => resolve(), config.retryDelay || 1000);
});
// 返回 Promise,延迟重试请求
return backoff.then(() => axiosInstance(config));
}
);
// 设置默认的重试次数和重试间隔
axiosInstance.defaults.retry = 3;
axiosInstance.defaults.retryDelay = 1000;
export default axiosInstance;
这个封装将自动重试逻辑整合到拦截器中,并通过 config.retry 来控制重试次数,config.retryDelay 控制重试的间隔时间。开发者可以根据需要灵活调整这些参数。
多环境支持的封装
在实际项目中,我们通常会根据开发、测试和生产环境对 API 请求的地址进行动态配置。可以通过环境变量来管理不同环境下的 baseURL。
const baseURL = process.env.NODE_ENV === 'production'
? 'https://api.example.com'
: 'http://localhost:3000';
const axiosInstance = axios.create({
baseURL,
timeout: 10000,
});
export default axiosInstance;
通过使用 process.env.NODE_ENV,可以确保 Axios 在不同环境下使用不同的 API 地址,这对于多环境开发非常实用。
错误处理与统一响应格式
在实际应用中,错误处理通常是重复而复杂的。通过封装,统一处理所有请求的错误和响应格式,能够极大减少开发中的出错机会,并提升开发效率。
axiosInstance.interceptors.response.use(
response => {
// 统一处理响应格式
const res = response.data;
if (res.code !== 200) {
console.error(res.message || 'Error');
return Promise.reject(new Error(res.message || 'Error'));
} else {
return res;
}
},
error => {
console.error('请求失败:', error);
return Promise.reject(error);
}
);
请求的取消与并发控制
有时我们需要在用户切换页面或重复发起请求时,取消之前的请求以避免不必要的网络开销。Axios 提供了 CancelToken 来实现请求取消。
const CancelToken = axios.CancelToken;
let cancel;
axiosInstance.get('/some-endpoint', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
// 在需要取消请求时调用
cancel();
并发控制则可以通过 axios.all() 来同时处理多个请求,并在所有请求完成时进行处理:
axios.all([
axiosInstance.get('/endpoint1'),
axiosInstance.get('/endpoint2')
])
.then(axios.spread((response1, response2) => {
console.log('response1:', response1);
console.log('response2:', response2);
}));
封装 Axios 实践总结
通过对 Axios 的封装,我们能够创建一个灵活、高效且具有一致性的 HTTP 请求库。封装后的 Axios 实例具备以下特点:
- 易于使用:我们可以方便地使用统一的请求方法,无需重复配置。
- 可维护性强:错误处理、请求拦截、响应拦截等逻辑集中管理,减少了重复代码,提高了可读性。
- 扩展性好:可以根据需求灵活添加功能,如重试机制、取消请求、并发控制等。
- 多环境支持:根据不同的环境配置 API 地址,提升了开发的灵活性。
参考资料
通过上述分析和实现,我们可以在实际项目中构建出一个高效且易于维护的 Axios 封装库,有助于提高整个项目的开发效率和代码质量。