IndexDB 浏览器服务器

news/2024/7/24 11:02:15 标签: 数据库, indexedDB, 前端, 浏览器

IndexDB 浏览器服务器

文章部分内容引用:

https://www.ruanyifeng.com/blog/2018/07/indexeddb.html

https://juejin.cn/post/7026900352968425486#heading-15

基本概念

  • 数据库:IDBDatabase 对象
  • 对象仓库:IDBObjectStore 对象
  • 索引: IDBIndex 对象
  • 事务: IDBTransaction 对象
  • 操作请求:IDBRequest 对象
  • 指针: IDBCursor 对象
  • 主键集合:IDBKeyRange 对象

(1)数据库

数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库

IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。

(2)对象仓库

每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。

(3)数据记录

对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。

{ id: 1, text: 'foo' }

上面的对象中,id属性可以当作主键。

数据体可以是任意数据类型,不限于对象。

(4)索引

为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。

(5)事务

数据记录的读写和删改,都要通过事务完成。事务对象提供errorabortcomplete三个事件,用来监听操作结果。

操作流程

IndexedDB 鼓励使用的基本模式如下所示:

  1. 打开数据库
  2. 数据库中创建一个对象仓库(object store)。
  3. 启动一个事务,并发送一个请求来执行一些数据库操作,像增加或提取数据等。
  4. 通过监听正确类型的 DOM 事件以等待操作完成。
  5. 在操作结果上进行一些操作(可以在 request 对象中找到)

打开数据库

由于 IndexedDB 本身的规范还在持续演进中,当前的 IndexedDB 的实现还是使用浏览器前缀。

在规范更加稳定之前,浏览器厂商对于标准 IndexedDB API 可能都会有不同的实现。但是一旦大家对规范达成共识的话,厂商就会不带前缀标记地进行实现。

实际上一些实现已经移除了浏览器前缀:IE 10,Firefox 16 和 Chrome 24。

当使用前缀的时候,基于 Gecko 内核的浏览器使用 moz 前缀,基于 WebKit 内核的浏览器会使用 webkit 前缀。

window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;

使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()方法

var request = window.indexedDB.open(databaseName, version);

这里传入两个参数:

indexedDB.open()方法返回一个IDBRequest对象。这个对象通过三种事件errorsuccessupgradeneeded,处理打开数据库的操作结果。

(1)error 事件

error事件表示打开数据库失败。

 request.onerror = function (event) {
   console.log('数据库打开报错');
 };

(2)success 事件

success事件表示成功打开数据库

var db;
request.onsuccess = function (event) {
    db = request.result;
    console.log('数据库打开成功');
};

这时,通过request对象的result属性拿到数据库对象。

(3)upgradeneeded 事件

如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded

如果 onupgradeneeded事件成功执行完成,打开数据库请求的 onsuccess 处理函数会被触发。

var db;
request.onupgradeneeded = function (event) {
   db = event.target.result;
}

这时通过事件对象的target.result属性,拿到数据库实例。


/**
 * 打开数据库
 * @param {string} dbName 数据库的名字
 * @param {string} version 数据库的版本
 * @return {object} 该函数会返回一个数据库实例
 */
function openDB(dbName, storeName, version) {
  return new Promise((resolve, reject) => {
    //  兼容浏览器
    var indexedDB =
      window.indexedDB ||
      window.mozIndexedDB ||
      window.webkitIndexedDB ||
      window.msIndexedDB;

    let db;

    // 打开数据库,如果没有则被创建
    const request = indexedDB.open(dbName, (version = 1));

    // 数据库成功打开的回调
    request.onsuccess = function (event) {
      db = event.target.result;
      resolve(db);
      console.log("数据库成功打开");
    };

    // 数据库打开失败的回调
    request.onerror = function (event) {
      console.error("数据库打开失败");
    };

    request.onupgradeneeded = function (event) {
      console.log("数据库更新");
      db = event.target.result;

      // 更好的写法是先判断一下,这存储库是否存在,如果不存在再新建
      if (!db.objectStoreNames.contains("user")) {
        // 创建存储库
        const objectStore = db.createObjectStore("user", {
          keyPath: "userId", // 主键
          // autoIncrement:true // 实现自增
        });

        // 创建索引,在后面查询数据的时候可以根据索引查
        objectStore.createIndex("userid", "userId", { unique: true }); // unique是否唯一
        objectStore.createIndex("name", "name", { unique: false });
        objectStore.createIndex("age", "age", { unique: false });
      }
    };
  });
}

新增数据

新增数据指的是向对象仓库写入数据记录。这需要通过事务完成。

事务提供了三种模式:readonlyreadwrite 和 versionchange

想要修改数据库模式或结构——包括新建或删除对象仓库或索引,只能在 versionchange 事务中才能实现。

该事务由一个指定了 version 的 IDBFactory.open 方法启动。(在仍未实现最新标准的 WebKit 浏览器IDBFactory.open 方法只接受一个参数,即数据库的 name,这样你必须调用 IDBVersionChangeRequest.setVersion 来建立 versionchange 事务。

使用 readonly 或 readwrite 模式都可以从已存在的对象仓库里读取记录。但只有在 readwrite 事务中才能修改对象仓库。

你需要使用 IDBDatabase.transaction (en-US) 启动一个事务。该方法接受两个参数:storeNames (作用域,一个你想访问的对象仓库的数组),事务模式 mode(readonly 或 readwrite)。该方法返回一个包含 IDBIndex.objectStore (en-US) 方法的事务对象,使用 IDBIndex.objectStore (en-US) 你可以访问你的对象仓库。未指定 mode 时,默认为 readonly 模式。

  • error 事件是冒泡机制,所以事务会接收由它产生的所有请求所产生的错误。更微妙的一点,错误会中断它所处的事务。除非你在错误发生的第一时间就调用了 stopPropagation 并执行了其他操作来处理错误,不然整个事务将会回滚。
  • 如果你在事务中没有处理一个已发生的错误或者调用 abort 方法,那么该事务会被回滚,并触发 abort 事件。
  • 在所有请求完成后,事务的 complete 事件会被触发。
function addData(db, storeName, data) {
  const request = db
    .transaction([storeName], "readwrite")
    .objectStore("user")
    .add(data || { userId: 1, name: "张三", age: 18 });
  request.onsuccess = function (event) {
    console.log("数据写入成功");
  };

  request.onerror = function (event) {
    console.error("数据写入失败,原因:", event.target.error);
  };
}

通过主键删除数据

IDBObjectStore.delete()方法用于删除记录。

/**
 * 
 * @param {IDBObjectStore} db  数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 主键值 
 */

function remove(db,storeName,key) {
    var request = db.transaction([storeName], 'readwrite')
      .objectStore(storeName)
      .delete(key);
  
    request.onsuccess = function (event) {
      console.log('数据删除成功');
    };
}

更新数据

put方法接收一个数据对象。

/**
 * 更新数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} data 数据
 */
function updateDB(db, storeName, data) {
  var request = db
    .transaction([storeName], "readwrite") // 事务对象
    .objectStore(storeName) // 仓库对象
    .put(data);

  request.onsuccess = function () {
    console.log("数据更新成功");
  };

  request.onerror = function () {
    console.log("数据更新失败");
  };
}

通过主键读取数据

现在数据库里已经有了一些信息,你可以通过几种方法对它进行提取。首先是简单的 get()。你需要提供键来提取值,像这样:

主键即刚刚我们在创建数据库时声明的keyPath,通过主键只能查询出一条数据。

/**
 * 通过主键读取数据
 * @param {IDBObjectStore} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 主键值
 */
function getDataByKey(db, storeName, key) {
  return new Promise((resolve, reject) => {
    var transaction = db.transaction([storeName]); // 事务
    var objectStore = transaction.objectStore(storeName); // 仓库对象
    var request = objectStore.get(key); // 通过主键获取数据

    request.onerror = function (event) {
      console.log("事务失败");
    };

    request.onsuccess = function (event) {
      console.error("主键查询结果: ", request.result);
      resolve(request.result);
    };
  });
}

通过游标查询数据

/**
 * 通过游标读取数据
 * @param {IDBObjectStore} db 数据库实例
 * @param {string} storeName 仓库名称
 */
function cursorGetData(db, storeName) {
  let list = [];
  var store = db
    .transaction(storeName, "readwrite") // 事务
    .objectStore(storeName); // 仓库对象
  var request = store.openCursor(); // 指针对象
  // 游标开启成功,逐行读数据
  request.onsuccess = function (e) {
    var cursor = e.target.result;
    if (cursor) {
      // 必须要检查
      list.push(cursor.value);
      cursor.continue(); // 遍历了存储对象中的所有内容
    } else {
      console.log("游标读取的数据:", list);
    }
  };
}

上面函数开启了一个游标,然后逐行读取数据,存入数组,最终得到整个仓库的所有数据。

通过索引查询数据

// 首先,确定你已经在 request.onupgradeneeded 中创建了索引:
// objectStore.createIndex("name", "name");
// 否则你将得到 DOMException。

var index = objectStore.index("name");

index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
};

索引名称即我们创建仓库的时候创建的索引名称,也就是键值对中的键,最终会查询出所有满足我们传入函数索引值的数据。

/**
 * 通过索引读取数据
 * @param {IDBObjectStore} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 */
function getDataByIndex(db, storeName, indexName, indexValue) {
  var store = db.transaction(storeName, "readwrite").objectStore(storeName);
  var request = store.index(indexName).get(indexValue);
  request.onerror = function () {
    console.error("事务失败");
  };
  request.onsuccess = function (e) {
    var result = e.target.result;
    console.log("索引查询结果:", result);
  };
}

指定游标的范围和方向

如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 openCursor() 或 openKeyCursor()

// 仅匹配 "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");

// 匹配所有超过“Bill”的,包括“Bill”
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");

// 匹配所有超过“Bill”的,但不包括“Bill”
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

// 匹配所有不超过“Donna”的,但不包括“Donna”
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);

// 匹配所有在“Bill”和“Donna”之间的,但不包括“Donna”
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

// 使用其中的一个键范围,把它作为 openCursor()/openKeyCursor 的第一个参数
index.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // 当匹配时进行一些操作
    cursor.continue();
  }
};

删除数据库

/**
 * 删除数据库
 * @param {object} dbName 数据库名称
 */
function deleteDBAll(dbName) {
  console.log(dbName);
  let deleteRequest = window.indexedDB.deleteDatabase(dbName);
  deleteRequest.onerror = function (event) {
    console.log("删除失败");
  };
  deleteRequest.onsuccess = function (event) {
    console.log("删除成功");
  };
}

http://www.niftyadmin.cn/n/100676.html

相关文章

零基础如何入门网络安全(黑客)

我经常会看到这一类的问题: 学习XXX知识没效果;学习XXX技能没方向;学习XXX没办法入门; 给大家一个忠告,如果你完全没有基础的话,前期最好不要盲目去找资料学习,因为大部分人把资料收集好之后&a…

C语言运算符赋值运算符杂项运算符运算符优先级

赋值运算符 下表列出了 C 语言支持的赋值运算符: 运算符 描述 实例 简单的赋值运算符,把右边操作数的值赋给左边操作数 C A B 将把 A B 的值赋给 C 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C A 相当于 C …

前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-02

接上一篇:《前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-01》 上一篇说到,在Handler.js的this.options下面的代码,this.assetsExtractor new AssetsExtractor(this.options),表明op…

代码随想录第四十二天| ● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集

01背包问题 模型详解 描述 在一个容量有限的 背包里装若干物品,这些物品重量不同,价值不同。如何装使这些背包内物品价值最大。 (1)如果物品可以分割,直接用贪心算法,首先装价值密度最大的物品 &#xff…

网络协议丨HTTP使用TCP/IP协议传数据的过程

我们HTTP 利用 TCP/IP 协议栈逐层打包再拆包,实现了数据传输,但下面的细节是不可见的。 但即使这样说,TCP/IP的工作过程还是显得很抽象。 你可以把 HTTP 利用 TCP/IP 协议栈传输数据想象成一个发快递的过程。 假设你想要发一个快递给朋友&…

windows10 wsl子系统固定ip启动分配网卡法

WSL设置添加固定IP 在Win端添加一个固定IP 192.168.50.99 用于X-Server界面显示.在WSL端添加一个固定IP 192.168.50.16 用于和Win端通讯 在win端创建批处理文件 创建一个批处理文件 我的文件位置是D:\powershell\static_ip.bat 向vEthernet (WSL)网卡添加一个IP 192.168.50.…

【软件测试】从功能到自动化测试,测试人的进阶之路细节,这些必不可少......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 测试流程&#xff0…

Java方法间的调用

本文以静态方法和非静态方法进行区分。一,静态方法调用其他方法1.静态方法调用非静态方法无论是否在同一个类中,均需要通过对象调用。Son类:public class Son {public void Son2(){//Son类里的非静态方法System.out.println("Son类里的非…