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

介绍

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

主要特点包括:

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

image.png

与客户端存储方式对比

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

存储方式 存储容量 数据持久性 数据访问速度 数据存储位置 使用场景
LocalStorage 5MB – 10MB 持久性 浏览器本地 存储小型数据、会话信息、持久性缓存等
Cookies 4KB 持久性 较慢 浏览器本地 存储小型数据、会话信息、跨域通信等
内存 取决于设备 临时性 非常快 浏览器内存 临时数据存储、运行时缓存、临时状态管理等
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)是用于存储和管理数据对象的地方。它类似于关系型数据库中的表或文档数据库中的集合,但它是基于键值对的存储模型。

image.png

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

使用 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 语法来处理操作的结果

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYE3JceL' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片