====== 第十一部分:设计模式与架构 ====== ===== 11.1 封装层设计 ===== ===== 11.1.1 Promise 封装 ===== 原生 IndexedDB API 基于事件,代码冗长。Promise 封装让代码更简洁: class IndexedDBWrapper { constructor(dbName, version) { this.dbName = dbName; this.version = version; this.db = null; } open(upgradeCallback) { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); request.onupgradeneeded = (event) => { if (upgradeCallback) { upgradeCallback(event.target.result, event.target.transaction); } }; request.onsuccess = (event) => { this.db = event.target.result; resolve(this.db); }; request.onerror = (event) => reject(event.target.error); request.onblocked = () => reject(new Error('Database blocked')); }); } add(storeName, data) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.add(data); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } get(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const request = store.get(key); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } put(storeName, data) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.put(data); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } delete(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.delete(key); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } getAll(storeName, query, count) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const request = store.getAll(query, count); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } close() { if (this.db) { this.db.close(); this.db = null; } } } // 使用 const db = new IndexedDBWrapper('MyApp', 1); await db.open((database, transaction) => { if (!database.objectStoreNames.contains('users')) { const store = database.createObjectStore('users', { keyPath: 'id' }); store.createIndex('emailIndex', 'email', { unique: true }); } }); await db.add('users', { id: 1, name: '张三', email: 'zs@example.com' }); const user = await db.get('users', 1); console.log(user); ===== 11.2 Repository 模式 ===== class UserRepository { constructor(dbWrapper) { this.db = dbWrapper; this.storeName = 'users'; } async create(user) { user.createdAt = new Date().toISOString(); return this.db.add(this.storeName, user); } async findById(id) { return this.db.get(this.storeName, id); } async findByEmail(email) { return new Promise((resolve, reject) => { const transaction = this.db.db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const index = store.index('emailIndex'); const request = index.get(email); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async update(id, updates) { const user = await this.findById(id); if (!user) throw new Error('User not found'); Object.assign(user, updates, { updatedAt: new Date().toISOString() }); return this.db.put(this.storeName, user); } async delete(id) { return this.db.delete(this.storeName, id); } async findAll(options = {}) { const { page = 1, pageSize = 20, sortBy = 'createdAt' } = options; const all = await this.db.getAll(this.storeName); // 排序 all.sort((a, b) => { if (a[sortBy] < b[sortBy]) return -1; if (a[sortBy] > b[sortBy]) return 1; return 0; }); // 分页 const start = (page - 1) * pageSize; return all.slice(start, start + pageSize); } } // 使用 const userRepo = new UserRepository(db); const user = await userRepo.create({ name: '李四', email: 'ls@example.com' }); const found = await userRepo.findByEmail('ls@example.com'); ===== 11.3 事件驱动架构 ===== class IndexedDBEventEmitter { constructor() { this.listeners = new Map(); } on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event).push(callback); } emit(event, data) { if (this.listeners.has(event)) { this.listeners.get(event).forEach(cb => cb(data)); } } } class ObservableDB extends IndexedDBWrapper { constructor(dbName, version) { super(dbName, version); this.events = new IndexedDBEventEmitter(); } async add(storeName, data) { const result = await super.add(storeName, data); this.events.emit('add', { storeName, data, key: result }); this.events.emit(`${storeName}:add`, { data, key: result }); return result; } async put(storeName, data) { const result = await super.put(storeName, data); this.events.emit('put', { storeName, data, key: result }); this.events.emit(`${storeName}:put`, { data, key: result }); return result; } async delete(storeName, key) { await super.delete(storeName, key); this.events.emit('delete', { storeName, key }); this.events.emit(`${storeName}:delete`, { key }); return key; } subscribe(storeName, event, callback) { this.events.on(`${storeName}:${event}`, callback); } } // 使用 const db = new ObservableDB('MyApp', 1); await db.open(/* ... */); // 监听用户数据变化 db.subscribe('users', 'add', (event) => { console.log('新用户添加:', event.data); }); db.subscribe('users', 'put', (event) => { console.log('用户更新:', event.data); }); ===== 11.4 数据同步模式 ===== class SyncManager { constructor(db, apiClient) { this.db = db; this.api = apiClient; this.syncQueue = []; } // 本地修改后排队同步 async localUpdate(storeName, data) { await this.db.put(storeName, { ...data, _syncStatus: 'pending' }); this.syncQueue.push({ action: 'update', storeName, data }); } async localDelete(storeName, key) { await this.db.put(storeName, { id: key, _syncStatus: 'pending_delete' }); this.syncQueue.push({ action: 'delete', storeName, key }); } // 执行同步 async sync() { for (const item of this.syncQueue) { try { if (item.action === 'update') { await this.api.update(item.storeName, item.data); } else if (item.action === 'delete') { await this.api.delete(item.storeName, item.key); } // 标记为已同步 await this.markSynced(item); } catch (error) { console.error('同步失败:', error); break; // 停止同步,下次重试 } } // 清空已处理的队列 this.syncQueue = this.syncQueue.filter(item => item._syncStatus !== 'synced'); } async markSynced(item) { if (item.action === 'delete') { await this.db.delete(item.storeName, item.key); } else { const data = await this.db.get(item.storeName, item.data.id); delete data._syncStatus; await this.db.put(item.storeName, data); } } // 从服务器拉取更新 async pullFromServer(lastSyncTime) { const updates = await this.api.getUpdates(lastSyncTime); for (const update of updates) { await this.db.put(update.storeName, update.data); } } } ===== 11.5 本章小结 ===== 本章介绍了 IndexedDB 的架构设计模式: * Promise 封装简化异步操作 * Repository 模式提供数据访问抽象 * 事件驱动架构实现数据变化监听 * 同步管理器实现离线-在线数据同步 继续阅读 [[part12-cases|第十二部分:实战案例]]。