本文介绍如何二次封装 axios,包括基本用法、配置拦截器、取消请求等功能,并提供一个支持错误重试、请求合并、状态管理的进阶封装方案,适合前端 hooks 场景使用。
import axios from 'axios';
axios
.get('https://api.example.com/data')
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.error(error);
});
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000
});
instance.interceptors.request.use((config) => {
// 从本地存储中获取token
const token = 'xxxx';
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
instance.interceptors.response.use((response) => {
if (response.type === 'blob') {
return response;
}
return response.data;
});
export default instance;
const cancelToken = axios.CancelToken;
const source = cancelToken.source();
axios
.get('https://api.example.com/data', { cancelToken: source.token })
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.error(error);
});
source.cancel('Operation canceled by the user.');
以下是一个更实用的 axios 封装方案,支持:
import axios from 'axios';
// 请求缓存池(用于请求合并)
const requestCache = new Map();
function serializeConfig(config) {
// 可根据实际需求自定义 key 生成方式
return `${config.method}-${config.url}-${JSON.stringify(
config.params || {}
)}-${JSON.stringify(config.data || {})}`;
}
export function useRequest(config, options = {}) {
const { manual = false, retry = 0 } = options;
let cancel;
let retryCount = 0;
const state = {
loading: false,
data: null,
error: null,
cancel: () => {
if (cancel) cancel('用户主动取消请求');
}
};
async function run(overrideConfig = {}) {
const finalConfig = { ...config, ...overrideConfig };
const cacheKey = serializeConfig(finalConfig);
if (requestCache.has(cacheKey)) {
return requestCache.get(cacheKey);
}
state.loading = true;
state.error = null;
state.data = null;
const source = axios.CancelToken.source();
cancel = source.cancel;
finalConfig.cancelToken = source.token;
const requestPromise = new Promise(async (resolve, reject) => {
while (retryCount <= retry) {
try {
const res = await axios(finalConfig);
state.data = res.data || res;
state.loading = false;
requestCache.delete(cacheKey);
resolve(state.data);
return;
} catch (err) {
if (axios.isCancel(err)) {
state.error = '请求已取消';
state.loading = false;
requestCache.delete(cacheKey);
reject(err);
return;
}
retryCount++;
if (retryCount > retry) {
state.error = err;
state.loading = false;
requestCache.delete(cacheKey);
reject(err);
return;
}
}
}
});
requestCache.set(cacheKey, requestPromise);
return requestPromise;
}
if (!manual) run();
return { ...state, run };
}
// 自动请求,失败重试 2 次
const { loading, data, error, run, cancel } = useRequest(
{
url: '/api/user',
method: 'get'
},
{ retry: 2 }
);
// 手动请求
// const { loading, data, error, run, cancel } = useRequest({ url: '/api/user' }, { manual: true });
// run();
// 取消请求
// cancel();
你可以根据实际业务进一步扩展,比如支持全局 loading、全局错误提示等。