import ageis from '@utils/aegis'; import { AegisEnum } from '@config/emun'; import { convertExpire } from '@utils/util'; import { createMemoryCache } from '@utils/cache/cache'; export type StorageOption = { /** * 命名空间 */ namespace?: string; /** * 设置缓存过期时间 */ expires?: number | string; /** * 设置缓存模式 */ memoryChace?: boolean; /** * 版本隔离 */ version?: string; }; export type SetStorageOption = Omit; /** * Creates a Storage instance that can be used by a WechatMiniprogram app. * 本地缓存数据工具 * * @param options - {@link StorageOption =} */ export function createStorage(config: StorageOption) { // eslint-disable-next-line @typescript-eslint/naming-convention const { namespace = '', memoryChace = false, version = '1.0.0' } = config; const cache = createMemoryCache(); function setExpires(expiresAt: number) { const timestamp = Date.now(); const expires = convertExpire(config?.expires || -1); if (expiresAt > 0) { return expiresAt + timestamp; } return expires > 0 ? expires + timestamp : expires; } function getCacheKey(key: string) { if (namespace) { return `${namespace}_${key}`; } return key; } /** * read cache * @param key cache key * @param defaultValue setting default value,if setting success, can't execute throw */ async function get(key: string, defaultValue?: any) { const cacheKey = getCacheKey(key); const cacheData = cache.get(cacheKey); // 优先获取内存缓存的数据 if (cacheData?.value) { // 判断当前版本与缓存版本是否一致,如版本不同,则删除旧缓存数据 if (cacheData?.version !== version) { remove(cacheKey); return defaultValue; } return cacheData?.value; } try { const { data } = await wx.getStorage({ key: cacheKey }); const { expires, value, memoryChace } = data; // 判断当前版本与缓存版本是否一致,如版本不同,则删除旧缓存数据 if (data?.version !== version) { remove(cacheKey); return defaultValue; } // 如设置过内存缓存,则自动设置内存中,方便下次读取 if (memoryChace) { const cacheExpiresAt = expires - Date.now(); // 计算剩余过期时间 if (cacheExpiresAt > 0) cache.set(cacheKey, data, cacheExpiresAt); } if (expires === -1 || expires > Date.now()) return value; if (defaultValue !== undefined) return defaultValue; remove(cacheKey); // delete expired data throw new Error(`Storage failed: Read '${key}' key cache data expired :(`); } catch (error) { if (defaultValue !== undefined) return defaultValue; remove(cacheKey); // delete expired data ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw new Error(`Storage failed: Read '${key}' key cache data expired :((`); } } /** * wirte cache * @param key cache key * @param value cache value * @param options */ async function set(key: string, value: any, options?: number | string | SetStorageOption) { const cacheKey = getCacheKey(key); const isMemoryCache = (options as SetStorageOption)?.memoryChace; const expires = typeof options === 'number' || typeof options === 'string' ? options : (options?.expires as number | string); const expiresAt = convertExpire(expires); const data = { value, expires: setExpires(expiresAt), createTime: Date.now(), version, memoryChace: isMemoryCache ?? memoryChace, }; if (isMemoryCache ?? memoryChace) { cache.set(cacheKey, data, expires ?? config.expires); } else { // 防止读取缓存时,获取到旧数据 console.log('delete memory cache data!!'); cache.remove(cacheKey); } try { return await wx.setStorage({ key: cacheKey, data }); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw error; } } /** * delete cache * @param key cache key */ async function remove(key: string) { try { const cacheKey = getCacheKey(key); return await wx.removeStorage({ key: cacheKey }); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw error; } } /** * clear all cache */ async function clear() { try { return await wx.clearStorage(); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}`); throw error; } } /** * sync read cache * @param key cache key * @param defaultValue setting default value,if setting success, can't execute throw */ function getSync(key: string, defaultValue?: any) { const cacheKey = getCacheKey(key); const cacheData = cache.get(cacheKey); // 优先获取内存缓存的数据 if (cacheData?.value) { // 判断当前版本与缓存版本是否一致,如版本不同,则删除旧缓存数据 if (cacheData?.version !== version) { removeSync(cacheKey); return defaultValue; } return cacheData?.value; } try { const data = wx.getStorageSync(cacheKey); const { expires, value } = data; // 判断当前版本与缓存版本是否一致,如版本不同,则删除旧缓存数据 if (data?.version !== version) { removeSync(cacheKey); return defaultValue; } // 如设置过内存缓存,则自动设置内存中,方便下次读取 if (memoryChace) { const cacheExpiresAt = expires - Date.now(); // 计算剩余过期时间 if (cacheExpiresAt > 0) cache.set(cacheKey, data, cacheExpiresAt); } if (expires === -1 || expires > Date.now()) return value; if (defaultValue !== undefined) return defaultValue; removeSync(cacheKey); throw new Error(`Storage failed: Read '${key}' key cache snyc data expired :(`); } catch (error) { removeSync(cacheKey); ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw new Error(`Storage failed: Read '${key}' key cache snyc data expired :(`); } } /** * sync wirte cache * @param key cache key * @param value cache value * @param options */ function setSync(key: string, value: any, options?: number | string | SetStorageOption) { const cacheKey = getCacheKey(key); const isMemoryCache = (options as SetStorageOption)?.memoryChace; const expires = typeof options === 'number' || typeof options === 'string' ? options : (options?.expires as number | string); const expiresAt = convertExpire(expires); const data = { value, expires: setExpires(expiresAt), createTime: Date.now(), version, memoryChace: isMemoryCache ?? memoryChace, }; try { // use memoryChace if (isMemoryCache ?? memoryChace) { cache.set(cacheKey, data, expires ?? config.expires); } else { // 防止读取缓存时,获取到旧数据 console.log('delete memory cache data!!'); cache.remove(cacheKey); } wx.setStorageSync(cacheKey, data); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw new Error(`Storage failed: Write '${key}' key cache snyc data :(`); } } /** * sync delete cache * @param key cache key */ function removeSync(key: string) { const cacheKey = getCacheKey(key); try { wx.removeStorageSync(cacheKey); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}, key: ${key}`); throw new Error(`Storage failed: Delete '${key}' key cache snyc data :(`); } } /** * sync clear all cache */ function clearSync() { try { return wx.clearStorageSync(); } catch (error) { ageis.infoAll(AegisEnum.STORAGE_ERROR_KEY, `${error.errMsg}`); throw new Error('Storage failed: Clear all cache snyc data :('); } } return { get, set, remove, clear, getSync, setSync, removeSync, clearSync, }; }