Tips & Tricks

IndexedDB Implementation:Claude Code 实战指南

了解indexeddb implementation:Claude Code 实战. 包含实用技巧和代码示例。

IndexedDBを選ぶ理由

localStorageは手軽ですが、容量制限(5MB程度)や数据構造の制約があります。IndexedDBは数百MB以上数据を構造化して保存でき、索引通过高速搜索も是可能的。借助 Claude Code,IndexedDBの複雑なAPIをラップした使いやすい数据アクセス層を构建可以。

类型安全なIndexedDBラッパー

> IndexedDBを类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类型安全に操作できるラッパー类を作って。
> CRUD操作と索引搜索に支持して。
interface StoreConfig {
  name: string;
  keyPath: string;
  indexes?: { name: string; keyPath: string; unique?: boolean }[];
}

class Database {
  private db: IDBDatabase | null = null;

  constructor(
    private name: string,
    private version: number,
    private stores: StoreConfig[]
  ) {}

  async open(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.name, this.version);

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        for (const store of this.stores) {
          if (!db.objectStoreNames.contains(store.name)) {
            const objectStore = db.createObjectStore(store.name, { keyPath: store.keyPath });
            store.indexes?.forEach((idx) => {
              objectStore.createIndex(idx.name, idx.keyPath, { unique: idx.unique });
            });
          }
        }
      };

      request.onsuccess = () => { this.db = request.result; resolve(); };
      request.onerror = () => reject(request.error);
    });
  }

  private getStore(name: string, mode: IDBTransactionMode) {
    if (!this.db) throw new Error('データベースが開かれていません');
    return this.db.transaction(name, mode).objectStore(name);
  }

  async put<T>(storeName: string, data: T): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = this.getStore(storeName, 'readwrite').put(data);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined> {
    return new Promise((resolve, reject) => {
      const request = this.getStore(storeName, 'readonly').get(key);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAll<T>(storeName: string): Promise<T[]> {
    return new Promise((resolve, reject) => {
      const request = this.getStore(storeName, 'readonly').getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async delete(storeName: string, key: IDBValidKey): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = this.getStore(storeName, 'readwrite').delete(key);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async findByIndex<T>(storeName: string, indexName: string, value: IDBValidKey): Promise<T[]> {
    return new Promise((resolve, reject) => {
      const index = this.getStore(storeName, 'readonly').index(indexName);
      const request = index.getAll(value);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
}

実際の应用での使用例

// 数据库定義
const db = new Database('myApp', 1, [
  {
    name: 'notes',
    keyPath: 'id',
    indexes: [
      { name: 'by-category', keyPath: 'category' },
      { name: 'by-updated', keyPath: 'updatedAt' },
    ],
  },
]);

// 初始化
await db.open();

// ノートの保存
await db.put('notes', {
  id: crypto.randomUUID(),
  title: '会議メモ',
  content: '本日の議題...',
  category: 'work',
  updatedAt: Date.now(),
});

// 分类で搜索
const workNotes = await db.findByIndex('notes', 'by-category', 'work');

服务器との同步

class SyncManager {
  private db: Database;

  constructor(db: Database) {
    this.db = db;
  }

  async syncToServer(storeName: string, apiEndpoint: string) {
    const pending = await this.db.findByIndex<any>(storeName, 'by-sync', 'pending');

    for (const item of pending) {
      try {
        await fetch(apiEndpoint, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(item),
        });
        await this.db.put(storeName, { ...item, syncStatus: 'synced' });
      } catch {
        console.log('同期失敗。オンライン復帰時に再試行します。');
        break;
      }
    }
  }
}

// オンライン復帰時に自動同步
window.addEventListener('online', () => {
  syncManager.syncToServer('notes', '/api/notes/sync');
});

Reactフックとの集成

function useIndexedDB<T>(storeName: string, key?: IDBValidKey) {
  const [data, setData] = useState<T | T[] | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    const load = key
      ? db.get<T>(storeName, key)
      : db.getAll<T>(storeName);

    load.then(setData).finally(() => setLoading(false));
  }, [storeName, key]);

  return { data, loading };
}

总结

借助 Claude Code,IndexedDBの複雑なAPIを类型安全にラップし、オフライン支持や服务器同步まで高效地实现可以。Service Workerとの联动はService Worker活用を、缓存戦略は缓存戦略

IndexedDB API的规范请参阅MDN Web Docs - IndexedDB

#Claude Code #IndexedDB #ローカルストレージ #offline #TypeScript