Vue3+Axios网络请求封装
本文将详细介绍一款基于Axios+Typescript封装的网络请求库,该库可以自动携带token、拦截请求和响应等操作,并能够处理请求重复、超时和错误状态码等异常情况。
介绍
Axios
是基于Node.js的HTTP客户端,也是一款广泛使用的网络请求库。它具有使用简单、可扩展性高、易用性好等特点,因此成为大多数前端开发者的首选,从而衍生了很多基于axios
封装的网络请求库。
今天我们要介绍的网络请求库是基于axios
封装的,命名为Axios
,它实现了自动携带token、拦截请求和响应等操作,能够处理请求重复、超时和错误状态码等异常情况。
代码说明
代码主要分为以下几个部分:
import
导入模块。在这个代码段中,我们导入了axios
和element-plus
两个模块,并对其进行了解构。- 定义两个接口,
CustomOptions
和LoadingOptions
。分别用于定义自定义配置和加载选项的接口,方便在调用Axios
方法时传递配置和选项。 - 定义了一个
Map
对象pendingMap
,用于储存每个请求的唯一回调函数。 - 定义了一个
LoadingInstance
对象,用于储存loading
实例和计数器。 getTokenAUTH()
方法用于获取token,该方法可以根据实际情况编写。Axios()
方法是Axios
库的核心方法,主要进行了请求和响应拦截,并处理了请求重复、超时和错误状态码等异常情况。httpErrorStatusHandle()
方法用于处理错误状态码。closeLoading()
方法用于关闭loading
层实例。addPending()
方法用于储存每个请求的唯一回调函数,以此为标识。removePending()
方法用于删除重复请求。getPendingKey()
方法用于生成每个请求的唯一key。
结论
Axios
封装的网络请求库,能够大大简化前端开发的工作,提高开发效率。本文介绍了该库的详细内容,希望对读者有所帮助。如果还有什么疑问或建议,欢迎在下方留言。
完整代码(中间件/网络请求拦截器-Typescript版本)
import axios, {AxiosRequestConfig, AxiosError, AxiosResponse, CancelToken} from 'axios';
import {ElLoading, ElMessage} from 'element-plus';
interface CustomOptions {
repeat_request_cancel?: boolean;
loading?: boolean;
restore_data_format?: boolean;
error_message_show?: boolean;
code_message_show?: boolean;
}
interface LoadingOptions {
fullscreen?: boolean;
text?: string;
customClass?: string;
spinner?: string;
background?: string;
}
const pendingMap: Map<string, CancelToken> = new Map();
interface LoadingInstance {
_target: any;
_count: number;
}
const LoadingInstance: LoadingInstance = {
_target: null,
_count: 0,
};
function getTokenAUTH(): string {
return "";
}
function Axios(
axiosConfig: AxiosRequestConfig,
customOptions: CustomOptions = {},
loadingOptions: LoadingOptions = {}
) {
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 1000 * 60,
responseType: 'json',
});
// 自定义配置
const custom_options = Object.assign(
{
repeat_request_cancel: true,
loading: false,
restore_data_format: true,
error_message_show: true,
code_message_show: false,
},
customOptions
);
// 请求拦截
service.interceptors.request.use(
(config: any) => {
removePending(config);
custom_options.repeat_request_cancel && addPending(config);
// 创建loading实例
if (custom_options.loading) {
LoadingInstance._count++;
if (LoadingInstance._count === 1) {
LoadingInstance._target = ElLoading.service(loadingOptions);
}
}
// 自动携带token
if (getTokenAUTH() && typeof window !== 'undefined') {
config.headers.Authorization = getTokenAUTH() as string;
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
(response: AxiosResponse) => {
removePending(response.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
if (
custom_options.code_message_show &&
response.data &&
response.data.code !== 200
) {
ElMessage({
type: 'error',
message: response.data.msg as string,
});
return Promise.reject(response.data); // code不等于200, 页面具体逻辑就不执行了
}
return custom_options.restore_data_format
? response.data
: response;
},
(error: AxiosError) => {
error.config && removePending(error.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
return Promise.reject(error); // 错误继续返回给到具体页面
}
);
return service(axiosConfig);
}
export default Axios;
/**
* 处理异常
* @param {*} error
*/
function httpErrorStatusHandle(error: any) {
// 处理被取消的请求
if (axios.isCancel(error)) return console.error('请求的重复请求:' + error.message);
let message = '';
if (error && error.response) {
switch (error.response.status) {
case 302:
message = '接口重定向了!';
break;
case 400:
message = '参数不正确!';
break;
case 401:
message = '您未登录,或者登录已经超时,请先登录!';
break;
case 403:
message = '您没有权限操作!';
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break; // 在正确域名下
case 408:
message = '请求超时!';
break;
case 409:
message = '系统已存在相同数据!';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '服务暂时无法访问,请稍后再试!';
break;
case 505:
message = 'HTTP版本不受支持!';
break;
default:
message = '异常问题,请联系管理员!';
break;
}
}
if (error.message.includes('timeout')) message = '网络请求超时!';
if (error.message.includes('Network'))
message = window.navigator.onLine ? '服务端异常!' : '您断网了!';
ElMessage({
type: 'error',
message,
});
}
/**
* 关闭Loading层实例
* @param {*} _options
*/
function closeLoading(_options: CustomOptions) {
if (_options.loading && LoadingInstance._count > 0)
LoadingInstance._count--;
if (LoadingInstance._count === 0) {
LoadingInstance._target.close();
LoadingInstance._target = null;
}
}
/**
* 储存每个请求的唯一cancel回调, 以此为标识
* @param {*} config
*/
function addPending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config);
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel: any) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel);
}
});
}
/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config);
if (pendingMap.has(pendingKey)) {
const cancelToken: any = pendingMap.get(pendingKey);
cancelToken(pendingKey);
pendingMap.delete(pendingKey);
}
}
/**
* 生成唯一的每个请求的唯一key
* @param {*} config
* @returns
*/
function getPendingKey(config: AxiosRequestConfig) {
const info: AxiosRequestConfig = config;
if (typeof info.data === 'string') info.data = JSON.parse(info.data); // response里面返回的config.data是个字符串对象
return [info.url, info.method, JSON.stringify(info.params), JSON.stringify(info.data)].join('&');
}
使用(Typescript版本)
import request from "@/utils/Request";
/**
* 获取专题
*/
export const GetSpecial = (data: object) => request({
url: '/list',
method: 'GET',
params: data
});
完整代码(中间件/网络请求拦截器-Js版本)
import axios from 'axios';
import {ElLoading, ElMessage} from 'element-plus';
const pendingMap = new Map();
const LoadingInstance = {
_target: null,
_count: 0
};
function getTokenAUTH() {
return null
}
function Axios(axiosConfig, customOptions, loadingOptions) {
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 1000 * 60,
responseType: 'json'
});
// 自定义配置
let custom_options = Object.assign({
repeat_request_cancel: true, // 是否开启取消重复请求, 默认为 true
loading: false, // 是否开启loading层效果, 默认为false
restore_data_format: true, // 是否开启简洁的数据结构响应, 默认为true
error_message_show: true, // 是否开启接口错误信息展示,默认为true
code_message_show: true, // 是否开启code不为0时的信息提示, 默认为false
}, customOptions);
// 请求拦截
service.interceptors.request.use(
config => {
removePending(config);
custom_options.repeat_request_cancel && addPending(config);
// 创建loading实例
if (custom_options.loading) {
LoadingInstance._count++;
if (LoadingInstance._count === 1) {
LoadingInstance._target = ElLoading.service(loadingOptions);
}
}
// 自动携带token
if (getTokenAUTH() && typeof window !== "undefined") {
config.headers.Authorization = getTokenAUTH();
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
response => {
removePending(response.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
if (custom_options.code_message_show && response.data && response.data.code !== 200) {
ElMessage({
type: 'error',
message: response.data.msg
})
return Promise.reject(response.data); // code不等于200, 页面具体逻辑就不执行了
}
return custom_options.restore_data_format ? response.data : response;
},
error => {
error.config && removePending(error.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
return Promise.reject(error); // 错误继续返回给到具体页面
}
);
return service(axiosConfig)
}
export default Axios;
/**
* 处理异常
* @param {*} error
*/
function httpErrorStatusHandle(error) {
// 处理被取消的请求
if (axios.isCancel(error)) return console.error('请求的重复请求:' + error.message);
let message = '';
if (error && error.response) {
switch (error.response.status) {
case 302:
message = '接口重定向了!';
break;
case 400:
message = '参数不正确!';
break;
case 401:
message = '您未登录,或者登录已经超时,请先登录!';
break;
case 403:
message = '您没有权限操作!';
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break; // 在正确域名下
case 408:
message = '请求超时!';
break;
case 409:
message = '系统已存在相同数据!';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '服务暂时无法访问,请稍后再试!';
break;
case 505:
message = 'HTTP版本不受支持!';
break;
default:
message = '异常问题,请联系管理员!';
break
}
}
if (error.message.includes('timeout')) message = '网络请求超时!';
if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常!' : '您断网了!';
ElMessage({
type: 'error',
message
})
}
/**
* 关闭Loading层实例
* @param {*} _options
*/
function closeLoading(_options) {
if (_options.loading && LoadingInstance._count > 0) LoadingInstance._count--;
if (LoadingInstance._count === 0) {
LoadingInstance._target.close();
LoadingInstance._target = null;
}
}
/**
* 储存每个请求的唯一cancel回调, 以此为标识
* @param {*} config
*/
function addPending(config) {
const pendingKey = getPendingKey(config);
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel);
}
});
}
/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config) {
const pendingKey = getPendingKey(config);
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey);
cancelToken(pendingKey);
pendingMap.delete(pendingKey);
}
}
/**
* 生成唯一的每个请求的唯一key
* @param {*} config
* @returns
*/
function getPendingKey(config) {
let {url, method, params, data} = config;
if (typeof data === 'string') data = JSON.parse(data); // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}
使用(Js版本)
import request from "@/utils/Request";
/**
* 获取专题
*/
export let GetSpecial = (data) => request({
url: '/list',
method: 'GET',
params: data
});
完结~
转载自:https://juejin.cn/post/7238604002354053176