You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在已有的IndexedDB数据库中新增对象仓库?

基于IndexedDB实现类localStorage的键值存储工具

我想实现一个基于IndexedDB、类似localStorage的简易键值存储工具,定义了LocalStorageAsync类,结构如下:

class LocalStorageAsync {
  constructor(storeName?: string)
  getItem(key: string): Promise<string>
  setItem(key: string, value: string): Promise<void>
}

实例化时,若未指定storeName则使用默认名称创建数据库,初始实现如下:

class LocalStorageAsync {
  //...

  constructor(storeName = 'default') {
    const openRequest = indexedDB.open('LocalStorageAsync')

    openRequest.onupgradeneeded = () => {
      const db = oRequest.result
      db.createObjectStore(storeKey)
    }

    this.#database = new Promise(resolve =>
      openRequest.onsuccess = () => resolve(oRequest.result)
  }

  //...
}

我希望通过storeName对数据库进行分区,即当数据库中不存在对应store时自动创建:

new LocalStorageAsync() // 会创建`LocalStorageAsync.default`
new LocalStorageAsync('foo') // 会创建`LocalStorageAsync.foo`

最初尝试在onsuccess回调中实现,但发现db.createObjectStore()只能在onupgradeneeded回调中调用,该回调仅在数据库版本升级或首次创建时触发。我不在意数据库版本,但需要触发onupgradeneeded来新增store。请问能否从onsuccess回调触发onupgradeneeded?比如直接升级数据库版本:

openRequest.onsuccess = () => {
  if (!db.objectStoreNames.contains(storeKey)) {
    // 非真实方法
    db.bumpVersion(db.version + 1)
  }
}

或者直接创建名称为LocalStorageAsync.${storeName}的全新数据库会更好?


解决方案分析

方案一:通过升级数据库版本触发onupgradeneeded创建新store

可以实现这个思路,但要遵循正确的版本升级流程:

  1. 在onsuccess回调中检查目标store是否存在,若不存在则关闭当前数据库连接,以当前版本+1的版本号重新打开数据库
  2. 在新的onupgradeneeded回调中创建缺失的store

示例代码:

class LocalStorageAsync {
  #database;
  #storeName;

  constructor(storeName = 'default') {
    this.#storeName = storeName;
    this.#database = new Promise((resolve, reject) => {
      const openRequest = indexedDB.open('LocalStorageAsync');

      openRequest.onsuccess = () => {
        const db = openRequest.result;
        if (db.objectStoreNames.contains(this.#storeName)) {
          resolve(db);
          return;
        }
        // 关闭当前连接,准备升级版本
        db.close();
        const upgradeRequest = indexedDB.open('LocalStorageAsync', db.version + 1);
        
        upgradeRequest.onupgradeneeded = () => {
          const upgradeDb = upgradeRequest.result;
          if (!upgradeDb.objectStoreNames.contains(this.#storeName)) {
            upgradeDb.createObjectStore(this.#storeName);
          }
        };
        
        upgradeRequest.onsuccess = () => resolve(upgradeRequest.result);
        upgradeRequest.onerror = () => reject(upgradeRequest.error);
      };

      openRequest.onupgradeneeded = () => {
        const db = openRequest.result;
        if (!db.objectStoreNames.contains(this.#storeName)) {
          db.createObjectStore(this.#storeName);
        }
      };

      openRequest.onerror = () => reject(openRequest.error);
    });
  }

  async getItem(key) {
    const db = await this.#database;
    const transaction = db.transaction(this.#storeName);
    const store = transaction.objectStore(this.#storeName);
    const request = store.get(key);
    return new Promise((resolve) => {
      request.onsuccess = () => resolve(request.result);
    });
  }

  async setItem(key, value) {
    const db = await this.#database;
    const transaction = db.transaction(this.#storeName, 'readwrite');
    const store = transaction.objectStore(this.#storeName);
    store.put(value, key);
    return new Promise((resolve, reject) => {
      transaction.oncomplete = () => resolve();
      transaction.onerror = () => reject(transaction.error);
    });
  }
}

这个方案的优势是所有store集中在同一个数据库,便于统一管理;缺点是每次新增store都要执行关闭连接、升级版本、重新打开的流程,会带来额外的操作开销。

方案二:每个store对应单独的数据库

直接将数据库名称设为LocalStorageAsync.${storeName},每个实例对应独立的数据库:

class LocalStorageAsync {
  #database;
  #dbName;

  constructor(storeName = 'default') {
    this.#dbName = `LocalStorageAsync.${storeName}`;
    this.#database = new Promise((resolve, reject) => {
      const openRequest = indexedDB.open(this.#dbName);

      openRequest.onupgradeneeded = () => {
        const db = openRequest.result;
        // 每个数据库只需要一个默认store
        db.createObjectStore('keyval');
      };

      openRequest.onsuccess = () => resolve(openRequest.result);
      openRequest.onerror = () => reject(openRequest.error);
    });
  }

  async getItem(key) {
    const db = await this.#database;
    const transaction = db.transaction('keyval');
    const store = transaction.objectStore('keyval');
    const request = store.get(key);
    return new Promise((resolve) => {
      request.onsuccess = () => resolve(request.result);
    });
  }

  async setItem(key, value) {
    const db = await this.#database;
    const transaction = db.transaction('keyval', 'readwrite');
    const store = transaction.objectStore('keyval');
    store.put(value, key);
    return new Promise((resolve, reject) => {
      transaction.oncomplete = () => resolve();
      transaction.onerror = () => reject(transaction.error);
    });
  }
}

这个方案的优势是逻辑简单,无需处理版本升级,每个实例的数据库完全独立,避免跨store的干扰;缺点是数据库数量会随storeName增多而增加,但IndexedDB对数据库数量的限制足够日常使用,一般不会出现问题。

方案选择

如果希望所有数据集中管理,选择方案一;如果追求实现简单、低维护成本,方案二更合适,也是同类工具的常见实现方式。

内容的提问来源于stack exchange,提问作者David Alsh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 04:00:38