浏览器本地存储是很常见的需求,例如:存储用户信息、主题信息等,我们一般使用localStorage或sesssionStorage可以很轻松的完成,但是如果使用不当,有时候也会给我们带来很多麻烦。接下来,我们实现一款功能强大的本地存储模块系统。 需求如下:
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>;
}
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;
}
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
};
}
}
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;
}
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
}
};
}
}
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
}
};
}
}
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
}
};
}
}
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
}
};
}
}
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;
}
}
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;
}
}
首先,我们来看一个完整的、类型安全的基本用法示例,它展示了从初始化到基本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();
对于敏感数据(如令牌、个人信息),可以集成加密功能。
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)
}
},
});
实现自动清理过期数据的机制,避免存储空间被无用数据占用。
tsclass 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小时后过期
在实际应用中,可能需要根据数据量或特性动态切换存储引擎。
tsasync 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);
}
}
展示如何注册和使用自定义存储引擎,满足特殊需求。
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'
});
添加监控逻辑,帮助开发者了解存储性能和数据状态。
tsinterface 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); // 每分钟报告一次
增强存储操作的健壮性,特别是对于不稳定的存储后端(如IndexedDB或网络存储)。
tsasync 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));
}


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