likes
comments
collection
share

IndexedDB: Web浏览器中的本地数据库

作者站长头像
站长
· 阅读数 64

介绍

IndexedDB是一种在Web浏览器中使用的本地数据库技术,它的用途广泛而多样。通过IndexedDB,Web应用可以网络连接的情况下,在客户端存储和检索大量结构化数据。

主要特点包括:

  1. 数据存储:IndexedDB允许创建多个数据库,每个数据库可以包含多个对象存储空间,类似于关系数据库中的表格。数据以键值对的形式存储,可以灵活地存储各种类型的数据。
  2. 异步操作:IndexedDB的API设计为异步执行,通过使用回调函数、事件监听或Promise等方式处理结果。这种异步模型可以避免阻塞主线程,提高应用程序的性能和响应能力。
  3. 支持事务。  IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

IndexedDB: Web浏览器中的本地数据库

与客户端存储方式对比

以下是LocalStorage、Cookies、内存和IndexedDB这四种客户端存储方式的比较:

存储方式存储容量数据持久性数据访问速度数据存储位置使用场景
LocalStorage5MB - 10MB持久性浏览器本地存储小型数据、会话信息、持久性缓存等
Cookies4KB持久性较慢浏览器本地存储小型数据、会话信息、跨域通信等
内存取决于设备临时性非常快浏览器内存临时数据存储、运行时缓存、临时状态管理等
IndexedDB取决于浏览器持久性中等浏览器本地存储大量结构化数据、离线应用、复杂数据查询等

数据库操作

创建和打开数据库

废话不多说,我们直接来创建一个数据库。

1.使用IndexedDB API中的open()方法打开(创建)数据库连接。此方法接受两个参数:数据库名称和版本号

const request = indexedDB.open('myDatabase', 1);

2.当数据库打开成功后,将触发success事件,可以在该事件的处理程序中进行后续操作。

request.onsuccess = function(event) {
  const db = event.target.result;
  // 进行数据库操作,如添加数据、查询数据等
};

3.如果打开数据库时发生错误,将触发error事件,可以在该事件的处理程序中处理错误情况。

request.onerror = function(event) { 
    console.log('Failed to open database'); 
};

版本管理和升级

当你需要修改数据库的结构,比如添加新的对象存储空间、修改现有的对象存储空间或索引等,就需要通过升级版本来执行这些变更操作。

当打开数据库时,如果指定的版本号高于现有数据库的版本号(在open()方法上指定版本),将触发upgradeneeded事件。在该事件的处理程序中,可以执行数据库结构和数据的变更操作。

request.onupgradeneeded = function(event) {
  const db = event.target.result;
  
  // 创建新的对象存储空间或修改现有的对象存储空间
  const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' });
  
  // 创建新的索引或修改现有的索引
  objectStore.createIndex('nameIndex', 'name', { unique: false });
};

如果在数据库升级过程中发生错误,将触发blocked事件。该事件表示有其他连接正在使用数据库,导致升级被阻塞。在这种情况下,你需要关闭其他连接才能继续升级。

request.onblocked = function(event) {
  console.log('Another connection is using the database');
};

删除数据库

使用indexedDB.deleteDatabase方法来删除数据库。该方法接受要删除的数据库名称作为参数,并返回一个IDBRequest对象,表示删除操作的异步请求。

// 删除数据库 
const deleteRequest = indexedDB.deleteDatabase("myDatabase");

对象存储空间(ObjectStore)

什么是对象存储空间?

在 IndexedDB 中,对象存储空间(Object Store)是用于存储和管理数据对象的地方。它类似于关系型数据库中的表或文档数据库中的集合,但它是基于键值对的存储模型。

IndexedDB: Web浏览器中的本地数据库

对象存储空间的创建和配置

使用 createObjectStore() 方法来创建对象存储空间,它接受两个参数:对象存储空间的名称和配置选项。

keyPath 参数指定了对象的键路径,这里我们使用自增的主键 "id"。通过设置 autoIncrementtrue,每次添加新的对象时,会自动为其生成一个唯一的自增值。


  // 创建对象存储空间
  const objectStore = db.createObjectStore("users", { keyPath: "id", autoIncrement: true });

  // 定义对象存储空间的属性,确保邮箱不会重复,所以我们使用 unique 

  objectStore.createIndex("nameIndex", "name", { unique: false });
  objectStore.createIndex("ageIndex", "age", { unique: false });
  objectStore.createIndex("emailIndex", "email", { unique: true });



为对象存储空间创建了三个索引:nameIndexageIndexemailIndex,分别用于姓名、年龄和电子邮件地址的索引。

数据的增删改查操作

你需要开启一个事务才能对你的创建的数据库进行操作。一旦你处于一个事务中,你就可以目标对象仓库发出请求。

通过db.transaction来创建一个事务,接受两个参数,第一个参数是要查询或者修改的对象存储空间数组,第二个参数是选择事务的模式,取决于你是要对数据库进行更改还是只需从中读取数据。事务提供了三种模式:readonlyreadwrite 和 versionchange

 // 获取事务
  const transaction = db.transaction(["users"], "readwrite");
  //根据事务获取对应得存储空间
  const objectStore = transaction.objectStore("users");

请注意,IndexedDB 中的事务不需要显式地调用 commit() 方法来提交更改。事务会自动提交,除非遇到错误或显式地调用 abort() 方法来中止事务。

以下是 IndexedDB 常用的增删改查操作的 API

操作API说明
添加数据objectStore.add(data)向对象存储空间中添加数据
读取数据objectStore.get(key)根据指定的键从对象存储空间中读取数据
更新数据objectStore.put(data)更新对象存储空间中的数据
删除数据objectStore.delete(key)根据指定的键从对象存储空间中删除数据

下面举个例子,展示一下增删改查数据


// 打开数据库成功
request.onsuccess = function(event) {
  const db = event.target.result;

  // 获取事务
  const transaction = db.transaction(["users"], "readwrite");
  const objectStore = transaction.objectStore("users");

  const userA = { name: "小A", age: 25, email: "xiaoA@example.com" };
  const userB = { name: "小B", age: 27, email: "xiaoB@example.com" };

  // 向用户信息表插入数据
  objectStore.add(userA);
  
  // 根据主键查询用户 
  const getRequest = objectStore.get(1); 
  
  getRequest.onsuccess = function(event) { 
          const user = event.target.result; 
          if (user) { 
              console.log("用户信息:", user); 
              
              // 更新用户的属性 
              user.age = 40;
              // 将更新后的用户保存回对象存储空间 
              const updateRequest = objectStore.put(user);
              
          } else { 
              console.log("找不到用户"); 
          } 
  };
  
  // 根据主键删除用户 
  const deleteRequest = objectStore.delete(1);


// 所有任务完成后触发oncomplete事件
  transaction.oncomplete = function() {
    console.log("数据插入成功!");
    db.close();
  };
};

发生错误或者需要回滚时,调用事务的 abort() 方法

let store = transaction.objectStore('users'); 
let request = store.add({ id: 1, name: '小C' }); 

// 发生错误或者需要回滚时,调用事务的 abort() 方法 
request.onerror = function(event) { 
    transaction.abort(); 
};

索引和游标

索引

索引在 IndexedDB 中扮演着非常重要的角色,它们可以提高数据检索的效率和灵活性。索引允许你通过特定属性或字段快速查找对象。通过创建索引,可以在执行查询时避免全表扫描,从而提高查询的速度。索引会为相应的属性创建一个数据结构,使得根据该属性进行查找更加高效。

使用对象存储空间的 createIndex() 方法创建索引。该方法接受三个参数:

  • 索引的名称:一个字符串,用于标识索引。
  • 索引的属性或键路径:一个字符串或字符串数组,指定要索引的属性或键路径。
  • 可选的配置对象:用于定义索引的配置选项,例如唯一性约束、多值索引等。
  // 创建索引
  objectStore.createIndex("nameIndex", "name", { unique: false });

游标(Cursor)

在 IndexedDB 中,没有像 SQL 中的 WHERE 条件语句那样直接的操作。但你可以使用游标(Cursor)来筛选和过滤数据,实现类似于 WHERE 的操作。

使用对象存储空间的 openCursor 方法打开一个游标。也可以使用索引对象的 openCursor 方法或对象存储空间的 openCursor 方法

 const transaction = db.transaction(["users"], "readwrite");
 const objectStore = transaction.objectStore("users");
 const nameIndex = objectStore.index("nameIndex");

const request = objectStore.openCursor();
// 或者使用 const request = nameIndex.openCursor();

request.onsuccess = function(event) {
  const cursor = event.target.result;
  if (cursor) {
    const key = cursor.key; // 获取键值
    const data = cursor.value; // 获取数据值
    // 处理数据
    if(data.id ===1){
        data.name = "New Name";
    }
    
    // 更新数据项 
    const updateRequest = cursor.update(data); 
    updateRequest.onsuccess = function() { 
        console.log("Data updated successfully."); 
    };
    cursor.continue(); // 移动到下一个数据项
  }
};

API总结

之后再总结一下常用的 IndexedDB API

方法描述
indexedDB.open()打开或创建一个 IndexedDB 数据库
db.createObjectStore()创建一个对象存储空间(表)
db.deleteObjectStore()删除指定的对象存储空间(表)
db.transaction()开启一个事务
db.onupgradeneeded数据库版本发生变化时的事件监听器
transaction.objectStore()获取指定的对象存储空间(表)
store.put()将数据存储到对象存储空间
store.add()将数据添加到对象存储空间
store.get()通过主键获取对象存储空间中的数据
store.delete()删除对象存储空间中指定主键的数据
store.clear()清空对象存储空间中的所有数据
store.count()获取对象存储空间中的记录数量
store.index()获取指定索引的对象
index.get()通过索引键获取对象存储空间中的数据
index.openCursor()打开索引的游标,用于遍历索引的结果集
cursor.continue()移动游标到下一个位置
cursor.update()更新游标当前位置的数据
cursor.delete()删除游标当前位置的数据
transaction.oncomplete所有任务完成时的事件监听器
transaction.onabort事务中止时的事件监听器
transaction.abort事务回滚

需要关注的一点是IndexedDB 的 API 都是异步的。这是因为 IndexedDB 在执行许多操作时需要与浏览器进行通信,并且这些操作可能涉及到磁盘读写等耗时操作,因此使用异步操作可以避免阻塞主线程,保持程序的响应性能。

IndexedDB 提供的异步 API 使用了 Promise 对象来处理操作结果。在执行 IndexedDB 的各种操作(如打开数据库、创建对象存储空间、插入、更新、删除数据等)时,会返回一个 Promise 对象。可以使用 Promise 的 then() 方法或 async/await 语法来处理操作的结果

转载自:https://juejin.cn/post/7250914419579093029
评论
请登录