2025-12-26
JavaScript
0

目录

1. 核心接口与类型定义
1.1 基础类型与存储接口
1.2 存储类型枚举与注册机制
2. 读写锁实现
2.1 读写锁管理器
3 存储引擎基类与内置实现
3.1 带锁的存储基类
3.2 LocalStorage实现
3.3 SessionStorage实现
3.4 IndexedDB实现
3.5 内存存储实现
4 存储工厂与注册机制
4.1 存储引擎注册表

浏览器本地存储是很常见的需求,例如:存储用户信息、主题信息等,我们一般使用localStorage或sesssionStorage可以很轻松的完成,但是如果使用不当,有时候也会给我们带来很多麻烦。接下来,我们实现一款功能强大的本地存储模块系统。 需求如下:

  1. 使用typescript,提供更好的代码类型提示
  2. 面向接口编程,IStore接口包括get、set、delete方法,支持异步(为了更好的适配indexdb)
  3. 需要支持多种存储方案(localStorage、sessionStorage、indexdb等等)每一种方案都需要实现上面的方法
  4. 初始化sotre时,指定存储方案(不支持向下兼容)
  5. 需要读写锁
  6. 也支持用户注册新的存储方案,只需要实现对应的接口即可

1. 核心接口与类型定义

1.1 基础类型与存储接口

typescript
/** * 存储键值对的数据结构 * @template T - 存储值的类型 */ export interface StorageItem<T = any> { value: T; timestamp: number; version?: string; expiry?: number; // 过期时间戳(新增) } /** * 存储配置选项 */ export interface StorageOptions { namespace?: string; timeout?: number; encryption?: { encrypt: (data: string) => string; decrypt: (data: string) => string; }; serialization?: { parse: (text: string) => any; stringify: (value: any) => string; }; } /** * 存储引擎信息 */ export interface StorageEngineInfo { type: string; support: { persistence: boolean; sizeLimit: number | 'unlimited'; transaction: boolean; indexed: boolean; }; } /** * 核心存储接口契约 * 所有存储引擎必须实现此接口 * 基于面向接口设计原则,确保统一的行为规范[6] */ export interface IStore<T = any> { /** * 获取存储项 * @param key - 存储键名 * @returns Promise包装的存储值或null */ get<K extends keyof T>(key: K): Promise<T[K] | null>; /** * 设置存储项 * @param key - 存储键名 * @param value - 存储值 * @param options - 附加选项 * @returns Promise包装的操作结果 */ set<K extends keyof T>(key: K, value: T[K], options?: { expiry?: number }): Promise<boolean>; /** * 删除存储项 * @param key - 存储键名 * @returns Promise包装的操作结果 */ delete<K extends keyof T>(key: K): Promise<boolean>; /** * 清空所有存储项 * @returns Promise包装的操作结果 */ clear(): Promise<boolean>; /** * 获取所有键名 * @returns Promise包装的键名数组 */ keys(): Promise<string[]>; /** * 检查键是否存在 * @param key - 存储键名 * @returns Promise包装的检查结果 */ has<K extends keyof T>(key: K): Promise<boolean>; /** * 获取存储引擎信息 */ getEngineInfo(): StorageEngineInfo; /** * 关闭存储连接(适用于IndexedDB等需要关闭的场景) */ close?(): Promise<void>; }

1.2 存储类型枚举与注册机制

typescript
/** * 内置存储类型枚举 */ export enum BuiltInStorageType { LOCAL_STORAGE = 'localStorage', SESSION_STORAGE = 'sessionStorage', INDEXED_DB = 'indexedDB', MEMORY = 'memory' } /** * 存储引擎构造函数接口 */ export interface StorageEngineConstructor { new <T>(options?: StorageOptions): IStore<T>; engineType: string; } /** * 存储引擎注册表项 */ export interface StorageEngineRegistryItem { type: string; constructor: StorageEngineConstructor; isBuiltIn: boolean; } /** * 存储模块配置 */ export interface StorageModuleConfig { defaultEngine: string; options?: StorageOptions; lockTimeout?: number; }

2. 读写锁实现

2.1 读写锁管理器

typescript
/** * 锁模式枚举 */ enum LockMode { READ = 'read', WRITE = 'write', NONE = 'none' } /** * 锁请求接口 */ interface LockRequest { resolve: (release: () => void) => void; reject: (error: Error) => void; mode: LockMode; timestamp: number; timeoutId?: NodeJS.Timeout; } /** * 读写锁实现 * 支持读共享、写独占的并发控制 * 基于Promise的异步锁机制[3][7] */ export class ReadWriteLock { private readLocks = 0; private writeLocked = false; private pendingQueue: LockRequest[] = []; private lockTimeout: number; constructor(timeout: number = 5000) { this.lockTimeout = timeout; } /** * 获取读锁 * 采用async/await语法,使异步代码更易读[2][3] */ async acquireRead(): Promise<() => void> { return new Promise((resolve, reject) => { const request: LockRequest = { resolve: (release) => { this.readLocks++; resolve(release); }, reject, mode: LockMode.READ, timestamp: Date.now() }; // 设置超时处理 request.timeoutId = setTimeout(() => { const index = this.pendingQueue.indexOf(request); if (index !== -1) { this.pendingQueue.splice(index, 1); reject(new Error('Read lock acquisition timeout')); } }, this.lockTimeout); this.pendingQueue.push(request); this.processQueue(); }); } /** * 获取写锁 */ async acquireWrite(): Promise<() => void> { return new Promise((resolve, reject) => { const request: LockRequest = { resolve: (release) => { this.writeLocked = true; resolve(release); }, reject, mode: LockMode.WRITE, timestamp: Date.now() }; // 设置超时处理 request.timeoutId = setTimeout(() => { const index = this.pendingQueue.indexOf(request); if (index !== -1) { this.pendingQueue.splice(index, 1); reject(new Error('Write lock acquisition timeout')); } }, this.lockTimeout); this.pendingQueue.push(request); this.processQueue(); }); } /** * 处理锁请求队列 */ private processQueue(): void { if (this.pendingQueue.length === 0) return; const request = this.pendingQueue[0]; if (request.mode === LockMode.READ) { // 读锁:没有写锁时可以获取 if (!this.writeLocked) { this.executeRequest(request); } } else if (request.mode === LockMode.WRITE) { // 写锁:没有读锁和写锁时可以获取 if (this.readLocks === 0 && !this.writeLocked) { this.executeRequest(request); } } } /** * 执行锁请求 */ private executeRequest(request: LockRequest): void { this.pendingQueue.shift(); if (request.timeoutId) { clearTimeout(request.timeoutId); } // 创建释放函数 const release = () => { if (request.mode === LockMode.READ) { this.releaseRead(); } else { this.releaseWrite(); } this.processQueue(); }; request.resolve(release); } /** * 释放读锁 */ private releaseRead(): void { this.readLocks = Math.max(0, this.readLocks - 1); } /** * 释放写锁 */ private releaseWrite(): void { this.writeLocked = false; } /** * 获取锁状态 */ getStatus() { return { readLocks: this.readLocks, writeLocked: this.writeLocked, pendingRequests: this.pendingQueue.length }; } }

3 存储引擎基类与内置实现

3.1 带锁的存储基类

typescript
/** * 带读写锁的存储基类 * 提供锁机制的统一封装 */ export abstract class BaseStore<T = any> implements IStore<T> { protected lock: ReadWriteLock; protected options: StorageOptions; protected namespace: string; constructor(options: StorageOptions = {}) { this.options = options; this.namespace = options.namespace || 'app-store'; this.lock = new ReadWriteLock(options.timeout || 5000); } /** * 带读锁保护的get方法 */ async get<K extends keyof T>(key: K): Promise<T[K] | null> { const release = await this.lock.acquireRead(); try { return await this.rawGet(key as string); } finally { release(); } } /** * 带写锁保护的set方法 */ async set<K extends keyof T>( key: K, value: T[K], options?: { expiry?: number } ): Promise<boolean> { const release = await this.lock.acquireWrite(); try { return await this.rawSet(key as string, value, options); } finally { release(); } } /** * 带写锁保护的delete方法 */ async delete<K extends keyof T>(key: K): Promise<boolean> { const release = await this.lock.acquireWrite(); try { return await this.rawDelete(key as string); } finally { release(); } } /** * 带写锁保护的clear方法 */ async clear(): Promise<boolean> { const release = await this.lock.acquireWrite(); try { return await this.rawClear(); } finally { release(); } } /** * 带读锁保护的keys方法 */ async keys(): Promise<string[]> { const release = await this.lock.acquireRead(); try { return await this.rawKeys(); } finally { release(); } } /** * 带读锁保护的has方法 */ async has<K extends keyof T>(key: K): Promise<boolean> { const release = await this.lock.acquireRead(); try { const result = await this.rawGet(key as string); return result !== null; } finally { release(); } } /** * 构建带命名空间的键名 */ protected buildKey(key: string): string { return `${this.namespace}:${key}`; } /** * 序列化数据 */ protected serialize(data: any): string { if (this.options.serialization) { return this.options.serialization.stringify(data); } return JSON.stringify({ value: data, timestamp: Date.now() }); } /** * 反序列化数据 */ protected deserialize<T>(text: string): StorageItem<T> | null { if (this.options.serialization) { return this.options.serialization.parse(text); } try { const item = JSON.parse(text) as StorageItem<T>; // 检查是否过期 if (item.expiry && Date.now() > item.expiry) { return null; } return item; } catch { return null; } } /** * 提取存储值 */ protected extractValue<T>(item: StorageItem<T> | null): T | null { return item ? item.value : null; } // 抽象方法,子类必须实现 protected abstract rawGet(key: string): Promise<any>; protected abstract rawSet(key: string, value: any, options?: { expiry?: number }): Promise<boolean>; protected abstract rawDelete(key: string): Promise<boolean>; protected abstract rawClear(): Promise<boolean>; protected abstract rawKeys(): Promise<string[]>; abstract getEngineInfo(): StorageEngineInfo; }

3.2 LocalStorage实现

typescript
/** * LocalStorage存储引擎 * 基于浏览器localStorage API实现 */ export class LocalStorageStore<T = any> extends BaseStore<T> { static engineType = BuiltInStorageType.LOCAL_STORAGE; protected async rawGet(key: string): Promise<any> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); const data = localStorage.getItem(fullKey); const item = this.deserialize(data || ''); resolve(this.extractValue(item)); } catch (error) { console.error('LocalStorage get error:', error); resolve(null); } }); } protected async rawSet( key: string, value: any, options?: { expiry?: number } ): Promise<boolean> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); const storageItem: StorageItem = { value, timestamp: Date.now(), expiry: options?.expiry }; const data = this.options.serialization ? this.options.serialization.stringify(storageItem) : JSON.stringify(storageItem); localStorage.setItem(fullKey, data); resolve(true); } catch (error) { console.error('LocalStorage set error:', error); resolve(false); } }); } protected async rawDelete(key: string): Promise<boolean> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); localStorage.removeItem(fullKey); resolve(true); } catch (error) { console.error('LocalStorage delete error:', error); resolve(false); } }); } protected async rawClear(): Promise<boolean> { return new Promise((resolve) => { try { const keysToRemove: string[] = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && key.startsWith(`${this.namespace}:`)) { keysToRemove.push(key); } } keysToRemove.forEach(key => localStorage.removeItem(key)); resolve(true); } catch (error) { console.error('LocalStorage clear error:', error); resolve(false); } }); } protected async rawKeys(): Promise<string[]> { return new Promise((resolve) => { try { const keys: string[] = []; const prefix = `${this.namespace}:`; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && key.startsWith(prefix)) { keys.push(key.substring(prefix.length)); } } resolve(keys); } catch (error) { console.error('LocalStorage keys error:', error); resolve([]); } }); } getEngineInfo(): StorageEngineInfo { return { type: BuiltInStorageType.LOCAL_STORAGE, support: { persistence: true, sizeLimit: 5 * 1024 * 1024, // 通常5MB限制 transaction: false, indexed: false } }; } }

3.3 SessionStorage实现

typescript
/** * SessionStorage存储引擎 * 实现方式与LocalStorage类似,但生命周期不同 */ export class SessionStorageStore<T = any> extends LocalStorageStore<T> { static engineType = BuiltInStorageType.SESSION_STORAGE; protected async rawGet(key: string): Promise<any> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); const data = sessionStorage.getItem(fullKey); const item = this.deserialize(data || ''); resolve(this.extractValue(item)); } catch (error) { console.error('SessionStorage get error:', error); resolve(null); } }); } protected async rawSet( key: string, value: any, options?: { expiry?: number } ): Promise<boolean> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); const storageItem: StorageItem = { value, timestamp: Date.now(), expiry: options?.expiry }; const data = this.options.serialization ? this.options.serialization.stringify(storageItem) : JSON.stringify(storageItem); sessionStorage.setItem(fullKey, data); resolve(true); } catch (error) { console.error('SessionStorage set error:', error); resolve(false); } }); } protected async rawDelete(key: string): Promise<boolean> { return new Promise((resolve) => { try { const fullKey = this.buildKey(key); sessionStorage.removeItem(fullKey); resolve(true); } catch (error) { console.error('SessionStorage delete error:', error); resolve(false); } }); } protected async rawClear(): Promise<boolean> { return new Promise((resolve) => { try { const keysToRemove: string[] = []; for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); if (key && key.startsWith(`${this.namespace}:`)) { keysToRemove.push(key); } } keysToRemove.forEach(key => sessionStorage.removeItem(key)); resolve(true); } catch (error) { console.error('SessionStorage clear error:', error); resolve(false); } }); } protected async rawKeys(): Promise<string[]> { return new Promise((resolve) => { try { const keys: string[] = []; const prefix = `${this.namespace}:`; for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); if (key && key.startsWith(prefix)) { keys.push(key.substring(prefix.length)); } } resolve(keys); } catch (error) { console.error('SessionStorage keys error:', error); resolve([]); } }); } getEngineInfo(): StorageEngineInfo { return { type: BuiltInStorageType.SESSION_STORAGE, support: { persistence: false, // 会话级别,不持久 sizeLimit: 5 * 1024 * 1024, transaction: false, indexed: false } }; } }

3.4 IndexedDB实现

TYPESCRIPT
/** * IndexedDB存储引擎 * 支持异步操作和更大存储空间[1][5] */ export class IndexedDBStore<T = any> extends BaseStore<T> { static engineType = BuiltInStorageType.INDEXED_DB; private dbName: string; private storeName: string; private db: IDBDatabase | null = null; private dbVersion = 1; constructor(options: StorageOptions = {}) { super(options); this.dbName = `${this.namespace}_db`; this.storeName = `${this.namespace}_store`; } /** * 连接数据库 */ private async connect(): Promise<IDBDatabase> { if (this.db) return this.db; return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.dbVersion); request.onerror = () => { reject(new Error('Failed to open IndexedDB')); }; request.onsuccess = () => { this.db = request.result; resolve(this.db); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName); } }; }); } protected async rawGet(key: string): Promise<any> { try { const db = await this.connect(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const fullKey = this.buildKey(key); const request = store.get(fullKey); request.onsuccess = () => { const data = request.result; const item = this.deserialize(data || ''); resolve(this.extractValue(item)); }; request.onerror = () => { reject(new Error('Failed to get data from IndexedDB')); }; }); } catch (error) { console.error('IndexedDB get error:', error); return null; } } protected async rawSet( key: string, value: any, options?: { expiry?: number } ): Promise<boolean> { try { const db = await this.connect(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const fullKey = this.buildKey(key); const storageItem: StorageItem = { value, timestamp: Date.now(), expiry: options?.expiry }; const data = this.options.serialization ? this.options.serialization.stringify(storageItem) : JSON.stringify(storageItem); const request = store.put(data, fullKey); request.onsuccess = () => { resolve(true); }; request.onerror = () => { reject(new Error('Failed to set data to IndexedDB')); }; }); } catch (error) { console.error('IndexedDB set error:', error); return false; } } protected async rawDelete(key: string): Promise<boolean> { try { const db = await this.connect(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const fullKey = this.buildKey(key); const request = store.delete(fullKey); request.onsuccess = () => { resolve(true); }; request.onerror = () => { reject(new Error('Failed to delete data from IndexedDB')); }; }); } catch (error) { console.error('IndexedDB delete error:', error); return false; } } protected async rawClear(): Promise<boolean> { try { const db = await this.connect(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.clear(); request.onsuccess = () => { resolve(true); }; request.onerror = () => { reject(new Error('Failed to clear IndexedDB store')); }; }); } catch (error) { console.error('IndexedDB clear error:', error); return false; } } protected async rawKeys(): Promise<string[]> { try { const db = await this.connect(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.getAllKeys(); const prefix = `${this.namespace}:`; request.onsuccess = () => { const keys = (request.result as string[]) .filter(key => key.startsWith(prefix)) .map(key => key.substring(prefix.length)); resolve(keys); }; request.onerror = () => { reject(new Error('Failed to get keys from IndexedDB')); }; }); } catch (error) { console.error('IndexedDB keys error:', error); return []; } } async close(): Promise<void> { if (this.db) { this.db.close(); this.db = null; } } getEngineInfo(): StorageEngineInfo { return { type: BuiltInStorageType.INDEXED_DB, support: { persistence: true, sizeLimit: 'unlimited', // 实际有上限,但通常足够大 transaction: true, indexed: true } }; } }

3.5 内存存储实现

typescript
/** * 内存存储引擎 * 用于测试和特定场景 */ export class MemoryStore<T = any> extends BaseStore<T> { static engineType = BuiltInStorageType.MEMORY; private storage = new Map<string, string>(); protected async rawGet(key: string): Promise<any> { return new Promise((resolve) => { const fullKey = this.buildKey(key); const data = this.storage.get(fullKey) || ''; const item = this.deserialize(data); resolve(this.extractValue(item)); }); } protected async rawSet( key: string, value: any, options?: { expiry?: number } ): Promise<boolean> { return new Promise((resolve) => { const fullKey = this.buildKey(key); const storageItem: StorageItem = { value, timestamp: Date.now(), expiry: options?.expiry }; const data = this.options.serialization ? this.options.serialization.stringify(storageItem) : JSON.stringify(storageItem); this.storage.set(fullKey, data); resolve(true); }); } protected async rawDelete(key: string): Promise<boolean> { return new Promise((resolve) => { const fullKey = this.buildKey(key); this.storage.delete(fullKey); resolve(true); }); } protected async rawClear(): Promise<boolean> { return new Promise((resolve) => { this.storage.clear(); resolve(true); }); } protected async rawKeys(): Promise<string[]> { return new Promise((resolve) => { const keys: string[] = []; const prefix = `${this.namespace}:`; this.storage.forEach((_, key) => { if (key.startsWith(prefix)) { keys.push(key.substring(prefix.length)); } }); resolve(keys); }); } getEngineInfo(): StorageEngineInfo { return { type: BuiltInStorageType.MEMORY, support: { persistence: false, sizeLimit: 'unlimited', transaction: false, indexed: false } }; } }

4 存储工厂与注册机制

4.1 存储引擎注册表

ts
/** * 存储引擎工厂 * 支持用户注册自定义存储引擎[6] */ export class StorageEngineFactory implements IStorageEngineFactory { private static instance: StorageEngineFactory; private registry = new Map<string, StorageEngineRegistryItem>(); private constructor() { // 注册内置存储引擎 this.registerBuiltInEngines(); } /** * 获取单例实例 */ static getInstance(): StorageEngineFactory { if (!StorageEngineFactory.instance) { StorageEngineFactory.instance = new StorageEngineFactory(); } return StorageEngineFactory.instance; } /** * 注册内置存储引擎 */ private registerBuiltInEngines(): void { this.registerEngine(LocalStorageStore, true); this.registerEngine(SessionStorageStore, true); this.registerEngine(IndexedDBStore, true); this.registerEngine(MemoryStore, true); } /** * 注册存储引擎(支持用户扩展) */ registerEngine( constructor: StorageEngineConstructor, isBuiltIn: boolean = false ): void { const type = constructor.engineType; if (this.registry.has(type)) { console.warn(`Storage engine "${type}" is already registered`); return; } this.registry.set(type, { type, constructor, isBuiltIn }); console.log(`Storage engine "${type}" registered successfully`); } /** * 注销存储引擎 */ unregisterEngine(type: string): boolean { const item = this.registry.get(type); if (item && !item.isBuiltIn) { return this.registry.delete(type); } if (item?.isBuiltIn) { console.warn(`Cannot unregister built-in storage engine: ${type}`); } return false; } /** * 创建存储引擎实例 */ createEngine<T>( type: string, options?: StorageOptions ): IStore<T> { const item = this.registry.get(type); if (!item) { throw new Error(`Storage engine "${type}" is not registered`); } try { return new item.constructor<T>(options); } catch (error) { throw new Error(`Failed to create storage engine "${type}": ${error.message}`); } } /** * 检测浏览器支持的存储类型 */ detectSupport(): Partial<Record<string, boolean>> { const support: Partial<Record<string, boolean>> = {}; // 检测localStorage try { localStorage.setItem('test', 'test'); localStorage.removeItem('test'); support[BuiltInStorageType.LOCAL_STORAGE] = true; } catch { support[BuiltInStorageType.LOCAL_STORAGE] = false; } // 检测sessionStorage try { sessionStorage.setItem('test', 'test'); sessionStorage.removeItem('test'); support[BuiltInStorageType.SESSION_STORAGE] = true; } catch { support[BuiltInStorageType.SESSION_STORAGE] = false; } // 检测IndexedDB support[BuiltInStorageType.INDEXED_DB] = !!window.indexedDB; // 内存存储始终可用 support[BuiltInStorageType.MEMORY] = true; return support; } /** * 获取已注册的引擎列表 */ getRegisteredEngines(): StorageEngineRegistryItem[] { return Array.from(this.registry.values()); } /** * 获取可用的存储引擎列表(基于浏览器支持) */ getAvailableEngines(): string[] { const support = this.detectSupport(); const engines: string[] = []; this.registry.forEach((item, type) => { if (support[type] !== false) { engines.push(type); } }); return engines; } }

4.2 主存储模块

ts
/** * 主存储模块 * 提供统一的API接口 */ export class StorageModule { private factory: StorageEngineFactory; private currentStore: IStore<any> | null = null; private config: StorageModuleConfig; constructor(config: StorageModuleConfig) { this.factory = StorageEngineFactory.getInstance(); this.config = config; // 初始化默认存储引擎 this.initialize(); } /** * 初始化存储 */ private async initialize(): Promise<void> { try { this.currentStore = this.factory.createEngine( this.config.defaultEngine, this.config.options ); console.log(`Storage initialized with engine: ${this.config.defaultEngine}`); } catch (error) { console.error('Failed to initialize storage:', error); throw error; } } /** * 获取当前存储实例 */ getStore<T = any>(): IStore<T> { if (!this.currentStore) { throw new Error('Storage not initialized'); } return this.currentStore as IStore<T>; } /** * 切换存储引擎 */ async switchEngine<T>(type: string, options?: StorageOptions): Promise<boolean> { try { // 关闭当前存储(如果支持) if (this.currentStore?.close) { await this.currentStore.close(); } // 创建新的存储实例 this.currentStore = this.factory.createEngine<T>(type, options || this.config.options); // 更新配置 this.config.defaultEngine = type; if (options) { this.config.options = { ...this.config.options, ...options }; } console.log(`Switched to storage engine: ${type}`); return true; } catch (error) { console.error(`Failed to switch to storage engine "${type}":`, error); // 尝试回退到原来的引擎 if (this.currentStore) { console.log('Reverting to previous storage engine'); } return false; } } /** * 注册自定义存储引擎 */ registerEngine(constructor: StorageEngineConstructor): void { this.factory.registerEngine(constructor, false); } /** * 获取存储引擎工厂(高级用法) */ getFactory(): StorageEngineFactory { return this.factory; } /** * 获取当前配置 */ getConfig(): StorageModuleConfig { return { ...this.config }; } /** * 获取当前存储引擎信息 */ getCurrentEngineInfo(): StorageEngineInfo | null { return this.currentStore?.getEngineInfo() || null; } }

5 使用示例与高级功能

5.1 基础使用示例

首先,我们来看一个完整的、类型安全的基本用法示例,它展示了从初始化到基本CRUD操作的全流程

ts
// 1. 定义存储数据类型(利用TypeScript实现类型安全) interface MyAppData { userProfile: { id: string; name: string; email: string; lastLogin: number; }; appSettings: { theme: 'light' | 'dark'; language: string; notifications: boolean; }; session: { token: string; expiresAt: number; }; } // 2. 初始化存储模块 const storageModule = new StorageModule({ defaultEngine: BuiltInStorageType.LOCAL_STORAGE, // 指定存储方案 options: { namespace: 'my-app-v1', // 避免与同一域名下其他应用冲突 timeout: 10000, // 操作超时10秒 }, }); // 获取强类型的存储实例 const store = storageModule.getStore<MyAppData>(); // 3. 基本的异步操作示例 async function demonstrateBasicUsage() { try { // 设置用户配置 await store.set('appSettings', { theme: 'dark', language: 'zh-CN', notifications: true, }); // 设置会话信息,并指定过期时间(1小时后) await store.set('session', { token: 'jwt-token-here', expiresAt: Date.now() + 60 * 60 * 1000, }, { expiry: Date.now() + 60 * 60 * 1000 }); // 可选:设置存储项本身的过期时间 // 读取数据(享受完整的TypeScript类型提示) const settings = await store.get('appSettings'); if (settings) { console.log(`当前主题: ${settings.theme}`); // TypeScript知道theme是'light' | 'dark'类型 // settings.unknownProperty; // TypeScript会报错:属性不存在 } // 检查数据是否存在 const hasUser = await store.has('userProfile'); console.log(`用户资料是否存在: ${hasUser}`); // 获取所有存储的键 const keys = await store.keys(); console.log('所有存储键:', keys); // 删除会话数据 await store.delete('session'); } catch (error) { console.error('存储操作失败:', error); } } // 执行示例 demonstrateBasicUsage();

5.2 高级功能与最佳实践

5.2.1 数据加密与安全存储

对于敏感数据(如令牌、个人信息),可以集成加密功能。

ts
// 简单的加密/解密工具(生产环境建议使用更强大的加密库) const simpleCrypto = { encrypt: (data: string): string => btoa(data), // Base64编码 decrypt: (data: string): string => atob(data), // Base64解码 }; // 使用加密配置初始化存储 const secureStorage = new StorageModule({ defaultEngine: BuiltInStorageType.LOCAL_STORAGE, options: { namespace: 'secure-app', encryption: simpleCrypto, // 传入加密配置 serialization: { // 自定义序列化,在加密前执行 stringify: (value) => JSON.stringify({ value, timestamp: Date.now(), // 可以添加数据签名防止篡改 // signature: generateSignature(value) }), parse: (text) => JSON.parse(text) } }, });

5.2.2 数据过期与自动清理

实现自动清理过期数据的机制,避免存储空间被无用数据占用。

ts
class StorageWithExpiry { private store: IStore<any>; constructor(store: IStore<any>) { this.store = store; } // 设置带过期时间的数据 async setWithExpiry<K extends keyof any>( key: K, value: any, ttl: number // 存活时间(毫秒) ): Promise<boolean> { return this.store.set(key, value, { expiry: Date.now() + ttl }); } // 获取数据并自动清理过期项 async getWithCleanup<K extends keyof any>(key: K): Promise<any> { const value = await this.store.get(key); // 如果返回null,可能已过期被内部清理 return value; } // 手动清理所有过期数据 async cleanupExpired(): Promise<string[]> { const keys = await this.store.keys(); const removed: string[] = []; for (const key of keys) { const value = await this.store.get(key); if (value === null) { // 过期项会返回null await this.store.delete(key); removed.push(key); } } return removed; } } // 使用示例 const expiryManager = new StorageWithExpiry(store); await expiryManager.setWithExpiry('tempData', { some: 'data' }, 24 * 60 * 60 * 1000); // 24小时后过期

5.2.3 存储引擎动态切换与数据迁移

在实际应用中,可能需要根据数据量或特性动态切换存储引擎。

ts
async function migrateData( fromEngine: string, toEngine: string, options?: StorageOptions ): Promise<boolean> { const sourceStore = storageModule.getFactory().createEngine(fromEngine, options); const targetStore = storageModule.getFactory().createEngine(toEngine, options); try { const keys = await sourceStore.keys(); // 分批迁移数据,避免内存溢出 const batchSize = 100; for (let i = 0; i < keys.length; i += batchSize) { const batchKeys = keys.slice(i, i + batchSize); // 并行读取数据 const values = await Promise.all( batchKeys.map(key => sourceStore.get(key)) ); // 并行写入新存储 await Promise.all( batchKeys.map((key, index) => values[index] !== null ? targetStore.set(key, values[index]) : Promise.resolve(false) ) ); } console.log(`数据迁移完成: ${fromEngine} -> ${toEngine}, 共迁移 ${keys.length} 项`); return true; } catch (error) { console.error('数据迁移失败:', error); return false; } finally { // 清理资源 if (sourceStore.close) await sourceStore.close(); if (targetStore.close) await targetStore.close(); } } // 根据数据量自动选择存储引擎 async function autoSelectStorageEngine(dataSizeThreshold: number = 500 * 1024) { const factory = storageModule.getFactory(); const support = factory.detectSupport(); // 检查当前数据量 const currentStore = storageModule.getStore(); const keys = await currentStore.keys(); let totalSize = 0; for (const key of keys) { const value = await currentStore.get(key); totalSize += JSON.stringify(value).length; } // 根据数据量选择存储引擎 if (totalSize > dataSizeThreshold && support[BuiltInStorageType.INDEXED_DB]) { console.log(`数据量较大(${totalSize} bytes),切换到IndexedDB`); await storageModule.switchEngine(BuiltInStorageType.INDEXED_DB); } else if (support[BuiltInStorageType.LOCAL_STORAGE]) { console.log(`数据量适中(${totalSize} bytes),使用LocalStorage`); await storageModule.switchEngine(BuiltInStorageType.LOCAL_STORAGE); } }

5.2.4 自定义存储引擎集成

展示如何注册和使用自定义存储引擎,满足特殊需求。

ts
// 实现一个基于服务器存储的自定义引擎(如RestAPI存储) class RestApiStore<T = any> extends BaseStore<T> { static engineType = 'rest-api'; private apiBaseUrl: string; constructor(options: StorageOptions & { apiBaseUrl: string }) { super(options); this.apiBaseUrl = options.apiBaseUrl; } protected async rawGet(key: string): Promise<any> { const response = await fetch(`${this.apiBaseUrl}/storage/${this.buildKey(key)}`); if (!response.ok) return null; const data = await response.json(); return this.extractValue(this.deserialize(JSON.stringify(data))); } protected async rawSet(key: string, value: any, options?: { expiry?: number }): Promise<boolean> { const storageItem: StorageItem = { value, timestamp: Date.now(), expiry: options?.expiry }; const response = await fetch(`${this.apiBaseUrl}/storage/${this.buildKey(key)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(storageItem) }); return response.ok; } // 实现其他必要方法... protected async rawDelete(key: string): Promise<boolean> { const response = await fetch(`${this.apiBaseUrl}/storage/${this.buildKey(key)}`, { method: 'DELETE' }); return response.ok; } protected async rawClear(): Promise<boolean> { // 通常服务器端不会实现全量清除,这里模拟实现 const keys = await this.rawKeys(); const results = await Promise.all(keys.map(key => this.rawDelete(key))); return results.every(result => result); } protected async rawKeys(): Promise<string[]> { const response = await fetch(`${this.apiBaseUrl}/storage/keys?prefix=${this.namespace}`); if (!response.ok) return []; const data = await response.json(); return data.keys || []; } getEngineInfo(): StorageEngineInfo { return { type: 'rest-api', support: { persistence: true, sizeLimit: 'unlimited', transaction: false, indexed: false } }; } } // 注册并使用自定义引擎 storageModule.registerEngine(RestApiStore); // 切换到自定义存储引擎 await storageModule.switchEngine('rest-api', { namespace: 'my-app', apiBaseUrl: 'https://api.example.com' });

5.2.5 性能监控与调试

添加监控逻辑,帮助开发者了解存储性能和数据状态。

ts
interface StorageMetrics { operation: 'get' | 'set' | 'delete' | 'clear'; key?: string; duration: number; success: boolean; timestamp: number; size?: number; } class MonitoredStore<T = any> implements IStore<T> { private store: IStore<T>; private metrics: StorageMetrics[] = []; private maxMetricsCount = 1000; constructor(store: IStore<T>) { this.store = store; } async get<K extends keyof T>(key: K): Promise<T[K] | null> { const start = performance.now(); try { const result = await this.store.get(key); this.recordMetric('get', performance.now() - start, true, key as string); return result; } catch (error) { this.recordMetric('get', performance.now() - start, false, key as string); throw error; } } // 为set、delete等方法实现类似的包装... private recordMetric( operation: StorageMetrics['operation'], duration: number, success: boolean, key?: string ) { this.metrics.push({ operation, duration, success, timestamp: Date.now(), key }); // 限制记录数量,避免内存泄漏 if (this.metrics.length > this.maxMetricsCount) { this.metrics = this.metrics.slice(-this.maxMetricsCount); } } getMetrics(): StorageMetrics[] { return [...this.metrics]; } getPerformanceReport() { const successfulOps = this.metrics.filter(m => m.success); const averageDuration = successfulOps.length > 0 ? successfulOps.reduce((sum, m) => sum + m.duration, 0) / successfulOps.length : 0; return { totalOperations: this.metrics.length, successRate: this.metrics.length > 0 ? (this.metrics.filter(m => m.success).length / this.metrics.length) * 100 : 100, averageDuration, operationsByType: this.metrics.reduce((acc, m) => { acc[m.operation] = (acc[m.operation] || 0) + 1; return acc; }, {} as Record<string, number>) }; } // 实现IStore的其他方法... } // 使用监控存储 const baseStore = storageModule.getStore<MyAppData>(); const monitoredStore = new MonitoredStore(baseStore); // 在应用适当的地方查看性能报告 setInterval(() => { console.log('存储性能报告:', monitoredStore.getPerformanceReport()); }, 60000); // 每分钟报告一次

5.2.6 错误处理与重试机制

增强存储操作的健壮性,特别是对于不稳定的存储后端(如IndexedDB或网络存储)。

ts
async function robustStorageOperation<T>( operation: () => Promise<T>, maxRetries: number = 3, baseDelay: number = 100 ): Promise<T> { let lastError: Error; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error as Error; console.warn(`存储操作失败,第${attempt + 1}次重试:`, error); if (attempt < maxRetries - 1) { // 指数退避策略 const delay = baseDelay * Math.pow(2, attempt); await new Promise(resolve => setTimeout(resolve, delay)); } } } throw new Error(`存储操作失败,已重试${maxRetries}次: ${lastError.message}`); } // 使用示例 async function reliableGet<K extends keyof MyAppData>(key: K): Promise<MyAppData[K] | null> { return robustStorageOperation(() => store.get(key)); } async function reliableSet<K extends keyof MyAppData>( key: K, value: MyAppData[K] ): Promise<boolean> { return robustStorageOperation(() => store.set(key, value)); }
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:繁星

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!