前沿 看了网上很多axios的封装,感觉都不是特别完善。于是我打算写个比较完整的封装包括以下功能(尚未全部完成,持续更新中):
- 上传下载文件时的header设置
- 错误相应的统一处理
- 动态加载api(已完成)
- 数据缓存(已完成)、清除缓存(已完成)、缓存级别(已完成)、最大缓存数(已完成)
- 拦截重复请求(已完成)、页面跳转时取消正在请求
- 刷新token
一、动态加载 请求每次使用时都需要先import对应的文件,比较麻烦;
// 引入
import { add } from "@/api/tool/code";
// 在方法中
add()
【axios|axios封装 —— 动态加载、数据缓存】这样做的好处就是不用在一开始就加载大量的api,不好的地方是每个页面都需要手动引入,尤其是一些比较通用的接口上,要再好多界面中引入。
利用Proxy对象可以实现api的自动加载,当然Proxy还可以用一些公用方法上,访问对象方法时自动加载。
let api = {};
api = new Proxy(api, {
// apiName是访问api下的对象名称也作为文件名称和文件里所有api的名字,在数据缓存中有相关处理
get(target, apiName) {
if (target[key] === undefined) { //在访问api的属性是判断是否有这个属性没有的话就按照名字和固定的路径去加载文件,然后挂载上去,最后返回。
console.log(target, apiName);
//加载路径可以根据自己的api放置位置和规则修改
let obj = require('./api/' + apiName + '.js').default;
target[apiName] = {};
for (let item in obj) {
target[key][item] = (data) => {
return request(obj[item](data), apiName);
}
}
}
return target[key];
}
})export default api;
//这里导出,最后挂载到全局
二、数据缓存与拦截重复请求
const service = axios.create({
// 设置请求超时时间
baseURL: '/api',
timeout: 20000,
headers: {
'content-type': 'application/json', // 设置请求体类型
/*
常用的请求格式有:
'application/json': 发送JSON对象
'multipart/form-data': 需要文件上传时,就使用该格式根据后台需求发送其他需要放在header里面数据,例如:
'Authorization': 'Basic ****', //登录验证
*/
},
// 默认情况下,后台状态码不返回200的话,是不会收到返回数据的,可以根据需要设置后台返回状态码的区间
validateStatus: function(status) {
return status >= 200 && status <= 500;
},
// 请求时是否携带cookie信息
withCredentials: true,
})// 定义最大缓存数量
const MAXCACHE = 5;
// 定义缓存对象
let caChe = {
list: [],
// 每个请求都有缓存级别,数字越小缓存级别越高
add(data) {
// 找到相同或小于当前级别的缓存的下标
let index = this.list.findIndex(item => {
return item.level ? data.level < item.level : true;
})
// 如果没有找到就插入到最前面,如果有就插入到这个缓存的前面
if (index <= 0) {
this.list.unshift(data);
} else {
this.list.splice(index, 0, data);
}
// 如果达到最大缓存数量,删除最后一个
if (this.list.length > MAXCACHE) {
this.list.pop();
}
},
// 找到这个key的缓存
find(key) {
return this.list.find(item => {
return key === item.key;
})
},
// 根据api名称删除缓存
deleteApiName(apiName) {
this.list = this.list.filter(item => {
return item.apiName !== apiName;
})
}
};
// 定义一个正在请求的列表,用来防止重复请求
let request_list = {};
let request = (params, apiName) => {
// 使用哈希MD5函数将 url、params、data 的数据组成一个key,这里使用HmacMd5就是不想这个key太长
let key = params.url + HmacMd5(JSON.stringify(params.params) + JSON.stringify(params.data)).toString();
console.log(key);
//如果request_list[key]有值则是判断为重复请求
if (request_list[key]) {
console.log('接口正在请求,等待返回');
return Promise.reject({
code: 301,
msg: '请求中,请稍等',
});
// 判断缓存中是否有,有的话直接返回
} else if (caChe.find(key) !== undefined) {
console.log('有缓存,直接返回数据');
return Promise.resolve(caChe.find(key).response_data);
} else {
return new Promise((resolve, reject) => {
console.log('没有缓存,发起请求');
// 添加key标识接口正在请求
request_list[key] = { apiName };
service(params).then(res => {resolve(res.data);
// 如果不是get请求就删除对应apiName的缓存,例如:
// 请求用户列表后缓存了返回数据,当我添加或修改了某一个用户后,在获取就应该请求数据而不是拿缓存中的数据;我这里的apiName为文件名
if (params.method !== 'get') {
caChe.deleteApiName(apiName);
} else if (params.isCache || params.isCache === undefined) {
// 判断接口是否需要缓存,接口默认为缓存
caChe.add({
response_data: res.data,
key,
apiName,
level: params.level,
})
}
}).catch(err => {
reject(err.data);
}).finally(() => {
// 不管成功与否都删除正在请求列表中对应的key,方便下次请求
delete request_list[key];
})
})
}
}
三、api封装文件
let user = {
get: ({ current, size }) => {
return {
url: '/user',
method: 'get',
isCache: true, // 是否缓存,不写默认为缓存
level: 0, // 缓存级别
params: { current, size }
}
},
post: (data) => {
return {
url: '/user',
method: 'post',
data: {
name: data.name,
age: data.age
}
}
},
put: ({ id, name, age }) => {
return {
url: '/user',
method: 'put',
data: { id, name, age },
}
},
delete: ({ id }) => {
return {
url: '/user',
method: 'delete',
params: { id: id },
}
}
}export default user;
推荐阅读
- 计算机网络|【前端vue——系列6】vue连接摄像头并实现摄像头暂停,计时,截图到本地等功能
- vue|vue调用浏览器摄像头并实现拍照、下载到本地功能
- vue|vue pc端调用摄像头进行拍照并实现裁剪上传
- Vue里调用本地摄像头进行拍照(简单记录)
- Vue|Vue 3 迁移策略笔记—— 第20节(Props 的默认值函数不能访问this)
- vue|Vue2 迁 Vue3 踩过的一些坑(持续更新)
- javascript|说一说实战项目升级从vue2到vue3 之main.js 区别
- vue 3初体验以及和vue 2的区别
- 前端|稍微聊聊vue3的函数式编程