axios请求的缓存与重复请求过滤的封装(防止频繁调取接口)
目录结构:
http-helper.ts
import axios, {CancelTokenSource, AxiosResponse, AxiosRequestConfig, AxiosError} from 'axios';
import Storage from './storage-helper';
import CryptoHelper from './cryptoJs-helper';
const CANCELTTYPE = {
CACHE: 1,
REPEAT: 2,
};
interface ICancel {
data: any;
type: number;
}
interface Request {
md5Key: string;
source: CancelTokenSource;
}
const pendingRequests: Request[] = [];
const http = axios.create();
const storage = new Storage();
const cryptoHelper = new CryptoHelper('cacheKey');
http.interceptors.request.use((config: AxiosRequestConfig) => {
/**
* 为每一次请求生成一个cancleToken
*/
const source = axios.CancelToken.source();
config.cancelToken = source.token;
/**
* 缓存命中判断
* 成功则取消当次请求
*/
const data = storage.get(cryptoHelper.encrypt(
config.url + JSON.stringify(config.data) + (config.method || ''),
));
if (data && (Date.now() <= data.exppries)) {
console.log(`接口:${config.url} 缓存命中 -- ${Date.now()} -- ${data.exppries}`);
source.cancel(JSON.stringify({
type: CANCELTTYPE.CACHE,
data: data.data,
}));
}
/**
* 重复请求判断
* 同url,同请求类型判定为重复请求
* 以最新的请求为准
*/
const md5Key = cryptoHelper.encrypt(config.url + (config.method || ''));
/**
* 将之前的重复且未完成的请求全部取消
*/
const hits = pendingRequests.filter((item) => item.md5Key === md5Key);
if (hits.length > 0) {
hits.forEach((item) => item.source.cancel(JSON.stringify({
type: CANCELTTYPE.REPEAT,
data: '重复请求,以取消',
})));
}
/**
* 将当前请求添加进请求对列中
*/
pendingRequests.push({
md5Key,
source,
});
return config;
});
http.interceptors.response.use((res: AxiosResponse) => {
/**
* 不论请求是否成功,
* 将本次完成的请求从请求队列中移除
*/
// 以同样的加密方式(MD5)获取加密字符串
const md5Key = cryptoHelper.encrypt(res.config.url + (res.config.method || ''));
const index = pendingRequests.findIndex((item) => item.md5Key === md5Key);
if (index > -1) {
pendingRequests.splice(index, 1);
}
if (res.data && res.data.type === 0) {
if (res.config.data) {
const dataParse = JSON.parse(res.config.data);
if (dataParse.cache) {
if (!dataParse.cacheTime) {
dataParse.cacheTime = 1000 * 60 * 3;
}
storage.set(cryptoHelper.encrypt(res.config.url + res.config.data + (res.config.method || '')), {
data: res.data.data,
exppries: Date.now() + dataParse.cacheTime,
});
console.log(`接口:${res.config.url} 设置缓存,缓存时间: ${dataParse.cacheTime}`);
}
}
return res.data.data;
} else {
return Promise.reject('接口报错了!');
}
});
/**
* 封装 get、post 请求
* 集成接口缓存过期机制
* 缓存过期将重新请求获取最新数据,并更新缓存
* 数据存储在localstorage
* {
* cache: true
* cacheTime: 1000 * 60 * 3 -- 默认缓存3分钟
* }
*/
const httpHelper = {
get(url: string, params: any) {
return new Promise((resolve, reject) => {
http.get(url, params).then(async (res: AxiosResponse) => {
resolve(res);
}).catch((error: AxiosError) => {
if (axios.isCancel(error)) {
const cancle: ICancel = JSON.parse(error.message);
if (cancle.type === CANCELTTYPE.REPEAT) {
return resolve([]);
} else {
return resolve(cancle.data);
}
} else {
return reject(error);
}
});
});
},
post(url: string, params: any) {
return new Promise((resolve, reject) => {
http.post(url, params).then(async (res: AxiosResponse) => {
resolve(res);
}).catch((error: AxiosError) => {
if (axios.isCancel(error)) {
const cancle: ICancel = JSON.parse(error.message);
if (cancle.type === CANCELTTYPE.REPEAT) {
return resolve(null);
} else {
return resolve(cancle.data);
}
} else {
return reject(error);
}
});
});
},
};
export default httpHelper;
cryptoJs-helper.ts 封装生成加密串
import cryptoJs from 'crypto-js';
class CryptoHelper {
public key: string;
constructor(key: string) {
/**
* 如需秘钥,可以在实例化时传入
*/
this.key = key;
}
/**
* 加密
* @param word
*/
public encrypt(word: string | undefined): string {
if (!word) {
return '';
}
const encrypted = cryptoJs.MD5(word);
return encrypted.toString();
}
}
export default CryptoHelper;
storage-helper.ts 封装数据存储
class Storage {
public get(key: string | undefined) {
if (!key) { return; }
const text = localStorage.getItem(key);
try {
if (text) {
return JSON.parse(text);
} else {
localStorage.removeItem(key);
return null;
}
} catch {
localStorage.removeItem(key);
return null;
}
}
public set(key: string | undefined, data: any) {
if (!key) {
return;
}
localStorage.setItem(key, JSON.stringify(data));
}
public remove(key: string | undefined) {
if (!key) {
return;
}
localStorage.removeItem(key);
}
}
export default Storage;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?