功能概述:
- 前端项目中有同一接口重复触发的并发现象
- 一些公共数据接口,如用户信息,配置信息等接口需要多次获取
- 首页有大量重复请求影响启动时间
适用条件:
- 基本的公共数据和用户信息
- get类获取数据接口
- 基本原则就是缓存不更新或者更新周期较长的数据
大体思路:
- 初次请求时将接口promise缓存到map对象中,map中的key名使用api名+参数拼接;下一次请求到同一key名的接口直接返回缓存中的promise;如果没有此key的promise使用正常的接口请求;
需求功能:
- 同一个接口,需要区分参数进行存储接口名+参数,生成唯一字符串
- 支持异步接口请求
- 支持promise
- 考虑并发的情况,同一接口同时触发的情况
- 默认关闭缓存,通过接口参数设置单独开启缓存
- 支持设置有效期,过期主动清除,取代惰性删除可能引起的内存溢出
实现代码:
/****************** promise方案封装 **********************/
class ItemCache {
constructor(promise, timeout) {
// data可存返回数据,也可存promise
this.promise = promise
// 设定超时时间,设定为多少秒
this.timeout = timeout
// 创建对象时候的时间,大约设定为数据获得的时间
this.cacheTime = (new Date()).getTime()
}
}
class ExpriesCache {
// 定义静态数据map来作为缓存池
static cacheMap = new Map()
// 数据是否超时
static isOverTime(name) {
const promise = ExpriesCache.cacheMap.get(name)
// 没有数据 判定超时
if (!promise) return true
// 获取系统当前时间戳
const currentTime = (new Date()).getTime()
// 获取当前时间与存储时间的过去的秒数
const overTime = (currentTime - promise.cacheTime) / 1000
// 如果过去的秒数大于当前的超时时间,也返回null让其去服务端取数据
if (Math.abs(overTime) > promise.timeout) {
// 惰性清除过期数据
ExpriesCache.cacheMap.delete(name)
return true
}
// 不超时
return false
}
// 当前data在 cache 中是否超时
static has(name) {
return !ExpriesCache.isOverTime(name)
}
// 删除 cache 中的 data
static delete(name) {
return ExpriesCache.cacheMap.delete(name)
}
// 获取缓存
static get(name) {
const isDataOverTiem = ExpriesCache.isOverTime(name)
console.log(isDataOverTiem)
//如果 数据超时,返回null,但是没有超时,返回promise
return isDataOverTiem ? null : ExpriesCache.cacheMap.get(name).promise
}
// 设置存储,默认过期时间600秒
static set(name, promise, timeout = 600) {
// 设置 itemCache
const itemCache = new ItemCache(promise, timeout)
// 缓存
ExpriesCache.cacheMap.set(name, itemCache)
// 定时器主动清除过期数据
setTimeout(()=>{
console.log('删除过期数据',name)
ExpriesCache.delete(name)
}, timeout*1000)
console.log(ExpriesCache.cacheMap)
}
// 声明key接口名 name+参数拼接
static getName(name,params) {
const paramsUrl = tool.paramsUrl(params)
const key = name + paramsUrl
return key
}
}
/****************** 缓存使用 **********************/
actions.forEach(item => {
let name = item.name || item.action;
api[name] = async params => {
params = params || {}
for (let i in params) {
params[i] = params[i] != 0 ? params[i] || '' : params[i];
}
let result = await api.getopenid();
let opendata = {
openid: result["openid"],
sign: result["sign"]
}
if (item.action === 'get_union_id' || item.action === 'wx_decode_data') {
opendata["session_key"] = result['session_key'];
}
// 是否开启缓存,获取属性后删除该参数
let isCache = params && params.isCache
params && params.isCache && delete params.isCache
// 生成key
let key = ExpriesCache.getName(name,params);
// 获得数据
let promise = ExpriesCache.get(key)
if(isCache && promise){
// 返回缓存数据******
console.log(key)
return promise
}else{
// 正常请求接口流程
const promise = new Promise((resolve, reject) => {
http.request({
url: item.url,
data: {
action: item.action,
t: Date.now(),
...opendata,
...params,
}
}).then(res => {
return resolve(res)
}).catch(err => {
hideLoading()
return reject(err)
})
})
// 设置promise缓存
isCache && ExpriesCache.set(key, promise, 10,)
return promise
}
}
})
注:该方案借鉴了其他技术大佬的方案,因大家发的都一样,不知道原作者是谁,故无备注参考文章地址