IndexedDB学习笔记

144 阅读4分钟

IndexedDB 是浏览器存储数据结构化数据的一个方案,用来代替已经废弃的Web SQL。另外,IndexedDB的几乎都是异步执行的。

  • IndexedDB 五大重点
    • 数据库(database):类似于MySQL等数据库,但与这些传统的数据库的区别在于 IndexedDB使用的是对象存储而不是表格存储
    • 对象存储(objectStore):创立数据库库之后,就要创建对象存储来存储数据。对象存储类似一张表,只不过存的是多个对象;
    • 事务(transaction):创建对象存储后,所有的操作都是通过事务完成的,比如对象存储的curd(增删改查);
    • 索引(index):用来快速访问对象存储上的每一项数据;
    • 游标:在对象存储中数据项中游走,可以用来获取其他项数据;

1. 使用数据库

首先检查浏览器是否支持 IndexedDB,如果支持则使用 indexedDB;

iOS 8对IndexedDB支持不友好,可以按不支持处理

   // 1. 检查浏览器是否支持 IndexedDB
    function idbOK() {
      return "indexedDB" in window && !/iPad|iPhone|iPod/.test(navigator.platform);
    }
    
    if (idbOK()) {
      let db;
      // 2. 打开数据库:echo_db为数据库名,1为版本号
      let openRequest = window.indexedDB.open('echo', 1); 
      
      // 3. 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
      openRequest.onupgradeneeded = function(event) {
        const thisDB = event.target.result; // event.target.result为数据库实例
        console.log('openRequest() onupgradeneeded', event.target.result);
      }

      // 4. 数据库打开成功
      openRequest.onsuccess = (event) => {
        db = event.target.result; // event.target.result为数据库实例
        console.log('openRequest() onsuccess', event.target.result);
      }

      // 5. 数据库打开失败
      openRequest.onerror = (event) => {
        console.log('openRequest() onerror', event.target.errorCode);
      }
    }

2. 使用对象存储

对象存储只能在 upgradeneeded 事件处理期间创建。因此第一次使用数据库,或者更新版本后才能创建新的对象存储;

// ...

// 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
openRequest.onupgradeneeded = function(event) {
    const thisDB = event.target.result; // event.target.result为数据库实例
    console.log('openRequest() onupgradeneeded', event.target.result);

    if(!thisDB.objectStoreNames.contains("users")) {
        // const store= thisDB.createObjectStore("users", { keyPath: "name" }); // 设置主键为‘name’
        const store= thisDB.createObjectStore("users", { keyPath: "id", autoIncrement:true }); // 主键名为id,且使用一个自增值自动为主键赋值
        store.createIndex("name", "name", { unique: true }); // 设置索引
    }
}

// ...
  • 索引:用来查询对象存储中的数据项,加快检索的速度
    • store.createIndex(indexName, indexPropotyName(索引的数据项属性), {unique: true, ...}):创建新索引
    • store.indexNames:获取对象存储上所有的索引
    • store.index(indexName): 使用索引
    • store.deleteIndex(indexName): 删除索引
  • 主键:是唯一性的索引,避免对象存储中有相同的记录
    • 创建对象存储时,必须创建

3. 操作数据

image.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script type="text/javascript" src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
</head>
<body>
  <h2>新增</h2>
  <div>
    名字:
    <input type="text" id="name" placeholder="name">
    <br/>
    邮箱:
    <input type="email" id="email" placeholder="Email">
    <br/>
    <button id="addUser">Add Users</button>
  </div>
  <h2>删除</h2>
  <div>
    主键:
    <input type="number" id="del_id" placeholder="name">
    <button id="deleteUser">Delete Users</button>
  </div>
  <h2>修改</h2>
  <div>
    主键:
    <input type="number" id="edit_id" placeholder="name">
    <br/>
    名字:
    <input type="text" id="edit_name" placeholder="name">
    <br/>
    邮箱:
    <input type="email" id="edit_email" placeholder="Email">
    <br/>
    <button id="modifyUser">Modify Users</button>
  </div>
  <div>
    <h2>查询</h2>
    <div>
      主键:
      <input type="number" id="query_id" placeholder="name">
      <button id="queryUser">Query Users</button>
    </div>
  </div>
  <div>
    <h2>游标学习</h2>
    <div>
      主键:
      <input type="number" id="cursor_id" placeholder="name">
      <button id="cursorUser">Cursor</button>
    </div>
  </div>
  <script>
    function idbOK() {
      return "indexedDB" in window && !/iPad|iPhone|iPod/.test(navigator.platform);
    }
  
    if (idbOK()) {
      let db;
      let openRequest = window.indexedDB.open('echo', 1); // 打开数据库:echo_db为数据库名,1为版本号
      
      // 第一次使用数据库,upgradeneeded 事件会被触发,另外打开新版本也会
      openRequest.onupgradeneeded = function(event) {
        const thisDB = event.target.result; // event.target.result为数据库实例
        console.log('openRequest() onupgradeneeded', event.target.result);

        if(!thisDB.objectStoreNames.contains("users")) {
          // const store= thisDB.createObjectStore("users", { keyPath: "name" }); // 设置主键为‘name’
          const store= thisDB.createObjectStore("users", { keyPath: "id", autoIncrement:true }); // 主键名为id,且使用一个自增值自动为主键赋值
          store.createIndex("name", "name", { unique: true }); // 设置索引
        }
      }

      // 数据库打开成功
      openRequest.onsuccess = (event) => {
        db = event.target.result; // event.target.result为数据库实例
        console.log('openRequest() onsuccess', event.target.result);

        // 开始监听按钮点击
        $("#addUser").on("click", addUser);
        $("#deleteUser").on("click", deleteUser);
        $("#modifyUser").on("click", modifyUser);
        $("#queryUser").on("click", queryUser);
        $("#cursorUser").on("click", cursorUser);
      }

      // 数据库打开失败
      openRequest.onerror = (event) => {
        console.log('openRequest() onerror', event.target.errorCode);
      }

      function addUser(e) {
        const name = $("#name").val();
        const email = $("#email").val();
        //事务处理
        const transaction = db.transaction(["users"],"readwrite"); // 1. transaction以下有展开描述

        // 获取名为users的对象存储 
        const store = transaction.objectStore("users"); // 2. store以下有展开描述
        //定义person
        const person = { 
          name,
          email,  
          created:new Date().getTime() 
        }
        //添加数据
        const request = store.add(person);
      
        // 错误处理器
        request.onerror = function(e) { 
          console.log("addUser(): onerror",e.target.error.name); 
        }
        request.onsuccess = function(e) { 
          console.log("addUser(): onsuccess");
        }
      }
      function deleteUser(e) {
        const key = +$("#del_id ").val();
        //事务处理
        const transaction = db.transaction(["users"],"readwrite");
        const store = transaction.objectStore("users");
        var request = store.delete(key);
      
        request.onerror = function(e) { 
          console.log("deleteUser(): onerror",e.target.error.name); 
        }
        request.onsuccess = function(e) { 
          console.log("deleteUser(): onsuccess");
        }
      }
      function modifyUser() {
        const id = +$("#edit_id").val();
        const name = $("#edit_name").val();
        const email = $("#edit_email").val();
        //事务处理
        const transaction = db.transaction(["users"],"readwrite");
        const store = transaction.objectStore("users"); // 获取名为users的对象存储
        //定义person
        const person = { 
          id,
          name,
          email,  
          created:new Date().getTime() 
        }
        //添加数据
        const request = store.put(person);
      
        // 错误处理器
        request.onerror = function(e) { 
          console.log("modifyUser(): onerror",e.target.error.name); 
        }
        request.onsuccess = function(e) { 
          console.log("modifyUser(): onsuccess,", e.target.result);
        }
      }
      function queryUser() {
        const key = +$("#query_id ").val();
        //事务处理
        const transaction = db.transaction(["users"],"readwrite");
        const store = transaction.objectStore("users");
        var request = store.get(key);
      
        request.onerror = function(e) { 
          console.log("queryUser(): onerror",e.target.error.name); 
        }
        request.onsuccess = function(e) { 
          console.log("queryUser(): onsuccess,", e.target.result);
        }
      }
      function cursorUser() {
        const key = +$("#query_id ").val();
        //事务处理
        const transaction = db.transaction("users");
        const store = transaction.objectStore("users");
        const request = store.openCursor();
      
        request.onerror = function(e) { 
          console.log("queryUser(): onerror",e.target.error.name); 
        }
        request.onsuccess = function(e) { 
          console.log("queryUser(): onsuccess,", e.target.result);
        }
      }
    }
  </script>
</body>
</html>
  1. transaction
    • 第一个属性:
      • 无: 获取所有对象存储;
      • objectStoreName: 获取单个对象存储;
      • [objectStoreName, ...]: 多个对象存储
    • 第二个属性:
      • 无:默认值 readonly;
      • readwrite:读写;
      • versionchange:版本改变
  2. store
    • store.add(data): 添加数据
    • store.get(index): 根据主键获取数据
    • store.delete(index): 根据主键删除数据
    • store.put(data):修改数据(数据中需包含主键)

4. 游标

使用事务可以通过一个已知键取得一条记录。如果想取得多条数据,则需要在事务中创建一个游标。

你可以将游标想象成一只快乐的小海狸,它跑进对象存储,一次取回一条数据。它每取得一条数据就带回来给你,而你又要求它去取下一条。游标可以双向移动(海狸也可以),也可以被限制在一定的数据“范围”内。

function cursorUser() {
    const key = +$("#query_id ").val();
    //事务处理
    const transaction = db.transaction("users");
    const store = transaction.objectStore("users");
    const request = store.openCursor(); // 1. openCursor以下有展开描述

    request.onerror = function(e) { 
      console.log("queryUser(): onerror",e.target.error.name); 
    }
    request.onsuccess = function(e) { 
      const cursor = event.target.result;
      console.log("queryUser(): onsuccess,", cursor); // 2. cursor以下有展开描述
    }
}
  1. openCursor
    • 参数:可选
      • 第一个参数
        • IDBKeyRange.only(key):直接访问主键为key的记录
        • IDBKeyRange.lowerBound(key):游标从"key"这个键开始,直到最后(包含指定的键)
        • IDBKeyRange.lowerBound(key, true):游标从"key"的下一条记录开始,直到最后(不包含指定的键)
        • IDBKeyRange.upperBound(key):游标从头开始,到"key"记录为止(包含指定的键)
        • IDBKeyRange.upperBound(key, true):游标从头开始,到"key"的前一条记录为止(不包含指定的键)
        • IDBKeyRange.bound(start, end, isUninvoled?, isUninvoled?): 下限的键、上限的键、是否跳过下限、是否跳过上限
      • 第二个参数
        • prev: 向最后一条记录前进
        • prevunique: 向最后一条记录前进, 且跳过重复记录
        • next: 向第一条记录前进
        • nextunique: 向第一条记录前进, 且跳过重复记录
  2. cursor
    • 属性:
      • direction:字符串常量("next"、"nextunique"、"prev"、"prevunique"),表示游标的前进方向以及是否应该遍历所有重复的值;
      • key:对象的键。
      • value:实际的对象。
      • primaryKey:游标使用的键。可能是主键或索引键。
    • 方法:
      • continue(key?): 参数 key 是可选的。如果没有指定 key,游标就移动到下一条记录;如果指定了,则游标移动到指定的键
      • advance(count):游标向前移动指定的 count 条记录