/** * 高可用微信小游戏 Storage 封装 * * 特性: * - 同步/异步双支持 * - 自动 JSON 序列化/反序列化 * - 过期时间支持 * - 内存缓存层 * - 完善的异常处理 * - TypeScript 类型支持 */ /** 存储数据的包装结构 */ interface StorageWrapper { data: T; /** 过期时间戳,null 表示永不过期 */ expireAt: number | null; /** 创建时间戳 */ createAt: number; } /** Storage 配置选项 */ interface StorageOptions { /** 过期时间(毫秒),默认为 null 表示永不过期 */ expireTime?: number | null; /** 是否启用内存缓存,默认为 true */ enableCache?: boolean; } /** 默认配置 */ const DEFAULT_OPTIONS: Required = { expireTime: null, enableCache: true, }; /** 内存缓存 */ const memoryCache = new Map>(); /** * 检查数据是否过期 */ function isExpired(wrapper: StorageWrapper): boolean { if (wrapper.expireAt === null) { return false; } return Date.now() > wrapper.expireAt; } /** * 创建存储包装对象 */ function createWrapper(data: T, expireTime: number | null): StorageWrapper { const now = Date.now(); return { data, expireAt: expireTime ? now + expireTime : null, createAt: now, }; } /** * 安全地解析 JSON */ function safeJsonParse(str: string): StorageWrapper | null { try { return JSON.parse(str) as StorageWrapper; } catch { return null; } } // ==================== 同步 API ==================== /** * 同步设置存储数据 * @param key 存储键名 * @param data 存储数据 * @param options 配置选项 * @returns 是否成功 */ export function setStorage(key: string, data: T, options?: StorageOptions): boolean { const opts = { ...DEFAULT_OPTIONS, ...options }; const wrapper = createWrapper(data, opts.expireTime ?? null); try { tt.setStorageSync(key, JSON.stringify(wrapper)); // 更新内存缓存 if (opts.enableCache) { memoryCache.set(key, wrapper as StorageWrapper); } return true; } catch (error) { console.error(`[Storage] setStorage failed for key "${key}":`, error); return false; } } /** * 同步获取存储数据 * @param key 存储键名 * @param defaultValue 默认值(当数据不存在或过期时返回) * @param options 配置选项 * @returns 存储的数据或默认值 */ export function getStorage(key: string, defaultValue?: T, options?: StorageOptions): T | undefined { const opts = { ...DEFAULT_OPTIONS, ...options }; // 优先从内存缓存读取 if (opts.enableCache && memoryCache.has(key)) { const cached = memoryCache.get(key) as StorageWrapper; if (!isExpired(cached)) { return cached.data; } // 过期则清除缓存 memoryCache.delete(key); removeStorage(key); return defaultValue; } try { const raw = tt.getStorageSync(key); if (!raw) { return defaultValue; } const wrapper = safeJsonParse(raw); if (!wrapper) { // 兼容旧数据格式(非包装格式) return raw as T; } // 检查是否过期 if (isExpired(wrapper)) { removeStorage(key); return defaultValue; } // 更新内存缓存 if (opts.enableCache) { memoryCache.set(key, wrapper as StorageWrapper); } return wrapper.data; } catch (error) { console.error(`[Storage] getStorage failed for key "${key}":`, error); return defaultValue; } } /** * 同步删除存储数据 * @param key 存储键名 * @returns 是否成功 */ export function removeStorage(key: string): boolean { try { tt.removeStorageSync(key); memoryCache.delete(key); return true; } catch (error) { console.error(`[Storage] removeStorage failed for key "${key}":`, error); return false; } } /** * 同步清空所有存储数据 * @returns 是否成功 */ export function clearStorage(): boolean { try { tt.clearStorageSync(); memoryCache.clear(); return true; } catch (error) { console.error('[Storage] clearStorage failed:', error); return false; } } /** * 检查存储键是否存在且未过期 * @param key 存储键名 * @returns 是否存在 */ export function hasStorage(key: string): boolean { return getStorage(key) !== undefined; } // ==================== 异步 API ==================== /** * 异步设置存储数据 * @param key 存储键名 * @param data 存储数据 * @param options 配置选项 */ export function setStorageAsync(key: string, data: T, options?: StorageOptions): Promise { const opts = { ...DEFAULT_OPTIONS, ...options }; const wrapper = createWrapper(data, opts.expireTime ?? null); return new Promise((resolve, reject) => { tt.setStorage({ key, data: JSON.stringify(wrapper), success: () => { // 更新内存缓存 if (opts.enableCache) { memoryCache.set(key, wrapper as StorageWrapper); } resolve(); }, fail: (error) => { console.error(`[Storage] setStorageAsync failed for key "${key}":`, error); reject(error); }, }); }); } /** * 异步获取存储数据 * @param key 存储键名 * @param defaultValue 默认值(当数据不存在或过期时返回) * @param options 配置选项 */ export function getStorageAsync(key: string, defaultValue?: T, options?: StorageOptions): Promise { const opts = { ...DEFAULT_OPTIONS, ...options }; // 优先从内存缓存读取 if (opts.enableCache && memoryCache.has(key)) { const cached = memoryCache.get(key) as StorageWrapper; if (!isExpired(cached)) { return Promise.resolve(cached.data); } // 过期则清除缓存 memoryCache.delete(key); return removeStorageAsync(key).then(() => defaultValue); } return new Promise((resolve) => { tt.getStorage({ key, success: (res) => { const raw = res.data; if (!raw) { resolve(defaultValue); return; } const wrapper = safeJsonParse(raw); if (!wrapper) { // 兼容旧数据格式 resolve(raw as T); return; } // 检查是否过期 if (isExpired(wrapper)) { removeStorageAsync(key).then(() => resolve(defaultValue)); return; } // 更新内存缓存 if (opts.enableCache) { memoryCache.set(key, wrapper as StorageWrapper); } resolve(wrapper.data); }, fail: (error) => { console.error(`[Storage] getStorageAsync failed for key "${key}":`, error); resolve(defaultValue); }, }); }); } /** * 异步删除存储数据 * @param key 存储键名 */ export function removeStorageAsync(key: string): Promise { return new Promise((resolve, reject) => { tt.removeStorage({ key, success: () => { memoryCache.delete(key); resolve(); }, fail: (error) => { console.error(`[Storage] removeStorageAsync failed for key "${key}":`, error); reject(error); }, }); }); } /** * 异步清空所有存储数据 */ export function clearStorageAsync(): Promise { return new Promise((resolve, reject) => { tt.clearStorage({ success: () => { memoryCache.clear(); resolve(); }, fail: (error) => { console.error('[Storage] clearStorageAsync failed:', error); reject(error); }, }); }); } // ==================== 工具方法 ==================== /** * 获取存储信息 */ export function getStorageInfo(): any { try { return tt.getStorageInfoSync(); } catch (error) { console.error('[Storage] getStorageInfo failed:', error); return { keys: [], currentSize: 0, limitSize: 0 }; } } /** * 异步获取存储信息 */ export function getStorageInfoAsync(): Promise { return new Promise((resolve) => { tt.getStorageInfo({ success: (res) => resolve(res), fail: (error) => { console.error('[Storage] getStorageInfoAsync failed:', error); resolve({ keys: [], currentSize: 0, limitSize: 0 }); }, }); }); } /** * 清理所有过期数据 */ export function cleanExpiredStorage(): void { try { const { keys } = tt.getStorageInfoSync(); keys.forEach((key) => { try { const raw = tt.getStorageSync(key); if (raw) { const wrapper = safeJsonParse(raw); if (wrapper && isExpired(wrapper)) { removeStorage(key); } } } catch { // 忽略单个 key 的错误 } }); } catch (error) { console.error('[Storage] cleanExpiredStorage failed:', error); } } /** * 清理内存缓存 */ export function clearMemoryCache(): void { memoryCache.clear(); } /** * 获取内存缓存大小 */ export function getMemoryCacheSize(): number { return memoryCache.size; } // ==================== 导出默认实例 ==================== export default { // 同步 API set: setStorage, get: getStorage, remove: removeStorage, clear: clearStorage, has: hasStorage, // 异步 API setAsync: setStorageAsync, getAsync: getStorageAsync, removeAsync: removeStorageAsync, clearAsync: clearStorageAsync, // 工具方法 getInfo: getStorageInfo, getInfoAsync: getStorageInfoAsync, cleanExpired: cleanExpiredStorage, clearCache: clearMemoryCache, getCacheSize: getMemoryCacheSize, };