Why
问题点
- 跨组件、跨项目的重复请求较多 要求
- 对代码侵入性小,使用简单
- 支持各种框架
技术目标
缓存方案
- 内存
- webworker,
- localstorage,
- sessionstorage,
- indexdb
需要缓存的类型
- 同页面内相同接口重复请求
- 同页面内相同接口,不同参数的合并请求,如多条数据基于id的补全
- 微前端下不同tab页的相同请求
- 浏览器tab页的数据请求
交互形式
- 单页内重复请求过滤
- 微前端跨tab页实现
- 页面聚焦刷新
- 本地缓存更新
- ...
业内的实现方案
- useSWR 用于数据请求的 React Hooks 库 https://swr.vercel.app/zh-CN
项目中实现
基于请求参数缓存实现
/**
* promise缓存函数,默认会基于入参作为key进行缓存进行
* @param fun 返回值为promise的函数
* @returns 返回值为promise的函数
// demo 创建promise缓存
import { promiseCacher } from '@bui/lcp-tools';
import axios from 'axios';
function get(params) {
return axios.get('http://dev.xuelei.com/static/js/area/new.json')
}
const getData = promiseCacher(get);
// 调用
getData().then(res => {
console.log('1111', res);
});
getData().then(res => {
console.log('22222', res);
});
setTimeout(() => {
getData().then(res => {
console.log('3333', res);
});
}, 2000);
*/
import Events from 'events';
enum PromiseStatus {
Pending = 'pending',
Fulfilled = 'fulfilled',
Rejected = 'rejected',
}
export function promiseCacher<T, A>(fun: (...arg: any) => Promise<T>): Promise<T> {
const eventEmitter = new Events.EventEmitter();
const cache: Map<any, { state?: PromiseStatus; value?: any }> = new Map();
return (...arg: any[]) => {
const key = JSON.stringify(arg);
const result = cache.get(key) || {};
if (result.state === PromiseStatus.Fulfilled) {
return Promise.resolve(result.value);
}
if (result.state === PromiseStatus.Rejected) {
return Promise.reject(result.value);
}
if (!result.state) {
result.state = PromiseStatus.Pending;
cache.set(key, result);
fun(...arg).then((v: any) => {
result.state = PromiseStatus.Fulfilled;
result.value = v;
eventEmitter.emit(`resolve-${key}`, result.value);
}).catch((e: any) => {
result.state = PromiseStatus.Rejected;
result.value = e;
eventEmitter.emit('reject' + key, e);
}).finally(() => {
cache.set(key, result);
});
}
return new Promise((resolve, reject) => {
eventEmitter.once(`resolve-${key}`, resolve);
eventEmitter.once(`reject-${key}`, reject);
});
};
}