在一些不常见的项目配置信息里,我们如果不缓存到内存里的话,可能到一个页面就需要重新请求,造成api请求次数的消耗。对于这种变化小,使用地方多的,我们可以使用内存缓存处理,第一次从api取,后续从内存里取。
如果数据变化了,需要及时清理内存重新获取。
也可以使用 pinia 定义 store,这种方式我才写完,leader说这里先不处理,我只好找个地方存储一下这个方法。
/**
* 内存缓存 刷新页面即重新加载
* 支持防止重复请求、缓存过期、响应式数据等功能
*/
import { type Ref, ref, shallowRef } from 'vue'
import type { ApiError } from '@/models'
interface CacheOptions {
/** 缓存过期时间(毫秒),默认不过期 */
ttl?: number
/** 是否使用浅响应 */
shallow?: boolean
}
interface CacheEntry<T> {
data: T
timestamp: number
}
export default function useMemoryCache<K = string, V = any>(options: CacheOptions = {}) {
const { ttl, shallow = false } = options
const cache = new Map<K, CacheEntry<V>>()
const loadingMap = new Map<K, Promise<V>>()
const errorMap = new Map<K, ApiError | undefined>()
function has(key: K): boolean {
if (!cache.has(key)) { return false }
// 检查是否过期
if (ttl) {
const entry = cache.get(key)!
const now = Date.now()
if (now - entry.timestamp > ttl) {
cache.delete(key)
return false
}
}
return true
}
function get(key: K): V | undefined {
if (!has(key)) { return undefined }
return cache.get(key)!.data
}
function set(key: K, value: V): void {
cache.set(key, {
data: value,
timestamp: Date.now(),
})
}
function remove(key: K): void {
cache.delete(key)
loadingMap.delete(key)
}
function clear(): void {
cache.clear()
loadingMap.clear()
}
/**
* 获取或加载数据(防止重复请求)
*/
async function init(
key: K,
loader: () => Promise<V>,
): Promise<V> {
// 返回缓存
if (has(key)) {
return get(key)!
}
// 复用正在进行的请求
if (loadingMap.has(key)) {
return loadingMap.get(key)!
}
// 发起新请求
errorMap.delete(key)
const promise = loader()
.then((data) => {
set(key, data)
return data
}).catch((err: unknown) => {
errorMap.set(key, err as ApiError)
throw err
}).finally(() => {
loadingMap.delete(key)
})
loadingMap.set(key, promise)
return promise
}
async function reload(key: K, loader: () => Promise<V>): Promise<V> {
remove(key)
return init(key, loader)
}
/**
* 创建响应式数据(配合 getOrLoad 使用)
*/
function createReactive(key: K, defaultValue: V): Ref<V> {
const state = shallow ? shallowRef(defaultValue) : ref(defaultValue)
if (has(key)) {
state.value = get(key)!
}
return state as Ref<V>
}
function getError(key: K): ApiError | undefined {
return errorMap.get(key)
}
function getLoading(key: K): boolean {
return loadingMap.has(key)
}
return {
has,
get,
set,
remove,
clear,
init,
reload,
createReactive,
getError,
getLoading,
}
}评论
暂无评论,快来抢沙发吧!