IndexedDB 教程

IndexedDB 教程

IndexedDB 是一個(gè)基于 JavaScript 的面向?qū)ο蟮氖聞?wù)型數(shù)據(jù)庫(kù)。有了 LocalStorageCookies,為什么還要推出 indexedDB 呢?其實(shí)對(duì)于在瀏覽器里存儲(chǔ)數(shù)據(jù),可以使用 cookiesLocalStorage,但它們都是比較簡(jiǎn)單的技術(shù),而 IndexedDB 提供了類似數(shù)據(jù)庫(kù)風(fēng)格的數(shù)據(jù)存儲(chǔ)和使用方式。

LocalStorage 與 IndexedDB 區(qū)別也適用場(chǎng)景

LocalStorage 是用 key-value 鍵值模式存儲(chǔ)數(shù)據(jù),它存儲(chǔ)的數(shù)據(jù)都是字符串形式。如果你想讓 LocalStorage存儲(chǔ)對(duì)象,你需要借助 JSON.stringify()能將對(duì)象變成字符串形式,再用 JSON.parse()將字符串還原成對(duì)象,就是專門為小數(shù)量數(shù)據(jù)設(shè)計(jì)的,所以它的 api 設(shè)計(jì)為同步的。

IndexedDB 很適合存儲(chǔ)大量數(shù)據(jù),它的 API 是異步調(diào)用的。IndexedDB 使用索引存儲(chǔ)數(shù)據(jù),各種數(shù)據(jù)庫(kù)操作放在事務(wù)中執(zhí)行。IndexedDB 甚至還支持簡(jiǎn)單的數(shù)據(jù)類型。IndexedDBlocalstorage 強(qiáng)大得多,但它的 API 也相對(duì)復(fù)雜。對(duì)于簡(jiǎn)單的數(shù)據(jù),你應(yīng)該繼續(xù)使用 localstorage,但當(dāng)你希望存儲(chǔ)大量數(shù)據(jù)時(shí),IndexedDB 會(huì)明顯的更適合,IndexedDB 能提供你更為復(fù)雜的查詢數(shù)據(jù)的方式。

indexedDB 的特性

  • 對(duì)象倉(cāng)庫(kù)

有了數(shù)據(jù)庫(kù)后我們自然希望創(chuàng)建一個(gè)表用來(lái)存儲(chǔ)數(shù)據(jù),但 indexedDB 中沒(méi)有表的概念,而是 objectStore,一個(gè)數(shù)據(jù)庫(kù)中可以包含多個(gè) objectStore,objectStore 是一個(gè)靈活的數(shù)據(jù)結(jié)構(gòu),可以存放多種類型數(shù)據(jù)。也就是說(shuō)一個(gè) objectStore 相當(dāng)于一張表,里面存儲(chǔ)的每條數(shù)據(jù)和一個(gè)鍵相關(guān)聯(lián)。我們可以使用每條記錄中的某個(gè)指定字段作為鍵值(keyPath),也可以使用自動(dòng)生成的遞增數(shù)字作為鍵值(keyGenerator),也可以不指定。選擇鍵的類型不同,objectStore 可以存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)也有差異。

  • 事務(wù)性

在 indexedDB 中,每一個(gè)對(duì)數(shù)據(jù)庫(kù)操作是在一個(gè)事務(wù)的上下文中執(zhí)行的。事務(wù)范圍一次影響一個(gè)或多個(gè) object stores,你通過(guò)傳入一個(gè) object store 名字的數(shù)組到創(chuàng)建事務(wù)范圍的函數(shù)來(lái)定義。例如:db.transaction(storeName, 'readwrite'),創(chuàng)建事務(wù)的第二個(gè)參數(shù)是事務(wù)模式。當(dāng)請(qǐng)求一個(gè)事務(wù)時(shí),必須決定是按照只讀還是讀寫模式請(qǐng)求訪問(wèn)。

  • 基于請(qǐng)求

對(duì) indexedDB 數(shù)據(jù)庫(kù)的每次操作,描述為通過(guò)一個(gè)請(qǐng)求打開(kāi)數(shù)據(jù)庫(kù),訪問(wèn)一個(gè) object store,再繼續(xù)。IndexedDB API 天生是基于請(qǐng)求的,這也是 API 異步本性指示。對(duì)于你在數(shù)據(jù)庫(kù)執(zhí)行的每次操作,你必須首先為這個(gè)操作創(chuàng)建一個(gè)請(qǐng)求。當(dāng)請(qǐng)求完成,你可以響應(yīng)由請(qǐng)求結(jié)果產(chǎn)生的事件和錯(cuò)誤。

  • 異步

在 IndexedDB 大部分操作并不是我們常用的調(diào)用方法,返回結(jié)果的模式,而是請(qǐng)求—響應(yīng)的模式,所謂異步 API 是指并不是這條指令執(zhí)行完畢,我們就可以使用 request.result 來(lái)獲取 indexedDB 對(duì)象了,就像使用 ajax 一樣,語(yǔ)句執(zhí)行完并不代表已經(jīng)獲取到了對(duì)象,所以我們一般在其回調(diào)函數(shù)中處理。

幾個(gè)概念

快速入門 demo

  • 打開(kāi)數(shù)據(jù)庫(kù)實(shí)例。
var db; // 全局的indexedDB數(shù)據(jù)庫(kù)實(shí)例。

//1\. 獲取IDBFactory接口實(shí)例(文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory)
var indexedDB =
  window.indexedDB ||
  window.webkitIndexedDB ||
  window.mozIndexedDB ||
  window.msIndexedDB;

if (!indexedDB) {
  console.log('你的瀏覽器不支持IndexedDB');
}

// 2\. 通過(guò)IDBFactory接口的open方法打開(kāi)一個(gè)indexedDB的數(shù)據(jù)庫(kù)實(shí)例
// 第一個(gè)參數(shù): 數(shù)據(jù)庫(kù)的名字,第二個(gè)參數(shù):數(shù)據(jù)庫(kù)的版本。返回值是一個(gè):IDBRequest實(shí)例,此實(shí)例有onerror和onsuccess事件。
var IDBOpenDBRequest = indexedDB.open('demoDB', 1);

// 3\. 對(duì)打開(kāi)數(shù)據(jù)庫(kù)的事件進(jìn)行處理

// 打開(kāi)數(shù)據(jù)庫(kù)成功后,自動(dòng)調(diào)用onsuccess事件回調(diào)。
IDBOpenDBRequest.onsuccess = function(e) {};

// 打開(kāi)數(shù)據(jù)庫(kù)失敗
IDBOpenDBRequest.onerror = function(e) {
  console.log(e.currentTarget.error.message);
};

// 第一次打開(kāi)成功后或者版本有變化自動(dòng)執(zhí)行以下事件:一般用于初始化數(shù)據(jù)庫(kù)。
IDBOpenDBRequest.onupgradeneeded = function(e) {
  db = e.target.result; // 獲取到 demoDB對(duì)應(yīng)的 IDBDatabase實(shí)例,也就是我們的數(shù)據(jù)庫(kù)。

  if (!db.objectStoreNames.contains(personStore)) {
    //如果表格不存在,創(chuàng)建一個(gè)新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會(huì)返回一個(gè)對(duì)象(objectStore)
    // objectStore就相當(dāng)于數(shù)據(jù)庫(kù)中的一張表。IDBObjectStore類型。
    var objectStore = db.createObjectStore(personStore, {
      keyPath: 'id',
      autoIncrement: true
    });

    //指定可以被索引的字段,unique字段是否唯一。類型: IDBIndex
    objectStore.createIndex('name', 'name', {
      unique: true
    });
    objectStore.createIndex('phone', 'phone', {
      unique: false
    });
  }
  console.log('數(shù)據(jù)庫(kù)版本更改為: ' + dbVersion);
};
  • 數(shù)據(jù)庫(kù)的 objectStore 添加

indexedDB 的增刪改查的操作需要放到一個(gè)事務(wù)中進(jìn)行(推薦)

// 創(chuàng)建一個(gè)事務(wù),類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction
var transaction = db.transaction(personStore, 'readwrite');

// 通過(guò)事務(wù)來(lái)獲取IDBObjectStore
var store = transaction.objectStore(personStore);

// 往store表中添加數(shù)據(jù)
var addPersonRequest = store.add({
  name: '老馬',
  phone: '189111833',
  address: 'aicoder.com'
});

// 監(jiān)聽(tīng)添加成功事件
addPersonRequest.onsuccess = function(e) {
  console.log(e.target.result); // 打印添加成功數(shù)據(jù)的 主鍵(id)
};

// 監(jiān)聽(tīng)失敗事件
addPersonRequest.onerror = function(e) {
  console.log(e.target.error);
};
  • 數(shù)據(jù)庫(kù)的 objectStore 修改
// 創(chuàng)建一個(gè)事務(wù),類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction
var transaction = db.transaction(personStore, 'readwrite');

// 通過(guò)事務(wù)來(lái)獲取IDBObjectStore
var store = transaction.objectStore(personStore);
var person = {
  id: 6,
  name: 'lama',
  phone: '515154084',
  address: 'aicoder.com'
};

// 修改或者添加數(shù)據(jù)。 第一參數(shù)是要修改的數(shù)據(jù),第二個(gè)參數(shù)是主鍵(可省略)
var updatePersonRequest = store.get(6);

// 監(jiān)聽(tīng)添加成功事件
updatePersonRequest.onsuccess = function(e) {
  // var p = e.target.result;  // 要修改的原對(duì)象
  store.put(person);
};

// 監(jiān)聽(tīng)失敗事件
updatePersonRequest.onerror = function(e) {
  console.log(e.target.error);
};
  • 數(shù)據(jù)庫(kù)的 objectStore 刪除
// 創(chuàng)建一個(gè)事務(wù),類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction
var transaction = db.transaction(personStore, 'readwrite');

// 通過(guò)事務(wù)來(lái)獲取IDBObjectStore
var store = transaction.objectStore(personStore);
store.delete(6).onsuccess = function(e) {
  console.log(刪除成功!)
};
  • 根據(jù) id 獲取數(shù)據(jù)
// 創(chuàng)建一個(gè)事務(wù),類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction
var transaction = db.transaction(personStore, 'readwrite');

// 通過(guò)事務(wù)來(lái)獲取IDBObjectStore
var store = transaction.objectStore(personStore);
store.get(6).onsuccess = function(e) {
  console.log(刪除成功!)
};
  • 數(shù)據(jù)庫(kù)的 objectStore 游標(biāo)查詢
var trans = db.transaction(personStore, 'readwrite');
var store = trans.objectStore(personStore);
var cursorRequest = store.openCursor();
cursorRequest.onsuccess = function(e) {
  var cursor = e.target.result;
  if (cursor) {
    var html = template('tbTmpl', cursor.value);
    document.getElementById('tbd').innerHTML += html;
    cursor.continue(); // 游標(biāo)繼續(xù)往下 搜索,重復(fù)觸發(fā) onsuccess方法,如果到最后返回null
  }
};

完整案例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./lib/art_template.js"></script>
</head>
<body>
  <table>
    <tr>
      <td>
        <label for="name">用戶名</label>
      </td>
      <td>
        <input type="text" name="name" id="name">
      </td>
    </tr>
    <tr>
      <td>
        <label for="phone">電話</label>
      </td>
      <td>
        <input type="text" name="phone" id="phone">
      </td>
    </tr>
    <tr>
      <td>
        <label for="address">地址</label>
      </td>
      <td>
        <input type="text" name="address" id="address">
      </td>
    </tr>
  </table>
  <input type="button" value="添加用戶" id="btnAdd" onclick="addPerson()">
  <table>
    <thead>
      <tr>
        <th>id</th>
        <th>name</th>
        <th>address</th>
        <th>phone</th>
        <th>編輯</th>
      </tr>
    </thead>
    <tbody id="tbd">
    </tbody>
  </table>
  <script id="tbTmpl" type="text/html">
    <tr>
      <td>{{id}}</td>
      <td>{{name}}</td>
      <td>{{phone}}</td>
      <td>{{address}}</td>
      <td><a href="#">修改</a>
      <a href="#" onclick="delById({{id}})">刪除</a></td>
    </tr>
  </script>
  <script>
    var db, dbName = 'demoDb', dbVersion = 1, personStore = 'person';
    // 創(chuàng)建indexedDB對(duì)象,兼容各種瀏覽器
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
      console.log("你的瀏覽器不支持IndexedDB");
    }

    openIndexedDB(loadTableData);

    // 配合游標(biāo)遍歷表中數(shù)據(jù),并配合art-template生成html
    function loadTableData() {
      document.getElementById('tbd').innerHTML = "";
      var trans = db.transaction(personStore, 'readwrite');
      var store = trans.objectStore(personStore);
      var cursorRequest = store.openCursor();
      cursorRequest.onsuccess = function (e) {
        var cursor = e.target.result;
        if (cursor) {
          var html = template('tbTmpl', cursor.value);
          document.getElementById('tbd').innerHTML += html;
          cursor.continue(); // 游標(biāo)繼續(xù)往下 搜索,重復(fù)觸發(fā) onsuccess方法,如果到到返回null
        }
      }
    }

    function delById(id) {
      if (!db || !id) {
        return;
      }
      // 創(chuàng)建一個(gè)事務(wù)
      var transaction = db.transaction(personStore, 'readwrite');

      // 通過(guò)事務(wù)來(lái)獲取store
      var store = transaction.objectStore(personStore);

      // 刪除請(qǐng)求
      var delPersonRequest = store.delete(id);
      delPersonRequest.onsuccess = function (e) {
        loadTableData(); // 刪除成功后,重新加載數(shù)據(jù)
      }
      delPersonRequest.onerror = function (e) {
        console.log(e.target.error);
      }
    }

    // 添加用戶
    function addPerson() {
      if (!db) {
        return;
      }
      var pName = document.getElementById('name').value;
      var pPhone = document.getElementById('phone').value;
      var pAddress = document.getElementById('address').value;
      // 創(chuàng)建一個(gè)事務(wù)
      var transaction = db.transaction(personStore, 'readwrite');

      // 通過(guò)事務(wù)來(lái)獲取store
      var store = transaction.objectStore(personStore);

      var addPersonRequest = store.add({ name: pName, phone: pPhone, address: pAddress });
      addPersonRequest.onsuccess = function (e) {
        console.log(e.target);
        loadTableData(); // 添加成功后重新加載數(shù)據(jù)
      }
      addPersonRequest.onerror = function (e) {
        console.log(e.target.error);
      }
    }

    // 打開(kāi)數(shù)據(jù)庫(kù)
    function openIndexedDB(callback) {
      // 打開(kāi)一個(gè)數(shù)據(jù)庫(kù)
      var request = indexedDB.open(dbName, dbVersion);

      // 打開(kāi)失敗
      request.onerror = function (e) {
        console.log(e.currentTarget.error.message);
      };

      // 打開(kāi)成功!
      request.onsuccess = function (e) {
        db = e.target.result;
        console.log('成功打開(kāi)DB');
        callback();
      };

      // 打開(kāi)成功后,如果版本有變化自動(dòng)執(zhí)行以下事件
      request.onupgradeneeded = function (e) {
        var db = e.target.result;
        if (!db.objectStoreNames.contains(personStore)) {
          console.log("我需要?jiǎng)?chuàng)建一個(gè)新的存儲(chǔ)對(duì)象");
          //如果表格不存在,創(chuàng)建一個(gè)新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會(huì)返回一個(gè)對(duì)象(objectStore)
          var objectStore = db.createObjectStore(personStore, {
            keyPath: "id",
            autoIncrement: true
          });

          //指定可以被索引的字段,unique字段是否唯一, 指定索引可以加快搜索效率。
          objectStore.createIndex("name", "name", {
            unique: true
          });
          objectStore.createIndex("phone", "phone", {
            unique: false
          });
        }
        console.log('數(shù)據(jù)庫(kù)版本更改為: ' + dbVersion);
      };
    }
  </script>
</body>
</html>

另外一個(gè)封裝的案例

<!DOCTYPE HTML>
<html>
<head>
  <title>aicoder.com</title>
</head>
<body>
  <script type="text/javascript">
    function openDB(name, version) {
      var version = version || 1;
      var request = window.indexedDB.open(name, version);
      request.onerror = function (e) {
        console.log(e.currentTarget.error.message);
      };
      request.onsuccess = function (e) {
        myDB.db = e.target.result;
      };
      request.onupgradeneeded = function (e) {
        var db = e.target.result;
        if (!db.objectStoreNames.contains('students')) {
          var store = db.createObjectStore('students', { keyPath: 'id' });
          store.createIndex('nameIndex', 'name', { unique: true });
          store.createIndex('ageIndex', 'age', { unique: false });
        }
        console.log('DB version changed to ' + version);
      };
    }

    function closeDB(db) {
      db.close();
    }

    function deleteDB(name) {
      indexedDB.deleteDatabase(name);
    }

    function addData(db, storeName) {
      var transaction = db.transaction(storeName, 'readwrite');
      var store = transaction.objectStore(storeName);

      for (var i = 0; i < students.length; i++) {
        store.add(students[i]);
      }
    }

    function getDataByKey(db, storeName, value) {
      var transaction = db.transaction(storeName, 'readwrite');
      var store = transaction.objectStore(storeName);
      var request = store.get(value);
      request.onsuccess = function (e) {
        var student = e.target.result;
        console.log(student.name);
      };
    }

    function updateDataByKey(db, storeName, value) {
      var transaction = db.transaction(storeName, 'readwrite');
      var store = transaction.objectStore(storeName);
      var request = store.get(value);
      request.onsuccess = function (e) {
        var student = e.target.result;
        student.age = 35;
        store.put(student);
      };
    }

    function deleteDataByKey(db, storeName, value) {
      var transaction = db.transaction(storeName, 'readwrite');
      var store = transaction.objectStore(storeName);
      store.delete(value);
    }

    function clearObjectStore(db, storeName) {
      var transaction = db.transaction(storeName, 'readwrite');
      var store = transaction.objectStore(storeName);
      store.clear();
    }

    function deleteObjectStore(db, storeName) {
      var transaction = db.transaction(storeName, 'versionchange');
      db.deleteObjectStore(storeName);
    }

    function fetchStoreByCursor(db, storeName) {
      var transaction = db.transaction(storeName);
      var store = transaction.objectStore(storeName);
      var request = store.openCursor();
      request.onsuccess = function (e) {
        var cursor = e.target.result;
        if (cursor) {
          console.log(cursor.key);
          var currentStudent = cursor.value;
          console.log(currentStudent.name);
          cursor.continue();
        }
      };
    }

    function getDataByIndex(db, storeName) {
      var transaction = db.transaction(storeName);
      var store = transaction.objectStore(storeName);
      var index = store.index("ageIndex");
      index.get(26).onsuccess = function (e) {
        var student = e.target.result;
        console.log(student.id);
      }
    }

    function getMultipleData(db, storeName) {
      var transaction = db.transaction(storeName);
      var store = transaction.objectStore(storeName);
      var index = store.index("nameIndex");
      var request = index.openCursor(null, IDBCursor.prev);
      request.onsuccess = function (e) {
        var cursor = e.target.result;
        if (cursor) {
          var student = cursor.value;
          console.log(student.name);
          cursor.continue();
        }
      }
    }

    var myDB = {
      name: 'test',
      version: 1,
      db: null
    };

    var students = [{
      id: 1001,
      name: "Byron",
      age: 24
    }, {
      id: 1002,
      name: "Frank",
      age: 30
    }, {
      id: 1003,
      name: "Aaron",
      age: 26
    }, {
      id: 1004,
      name: "Casper",
      age: 26
    }];
  </script>
</body>
</html>

參考

  1. HTML5 本地存儲(chǔ)——IndexedDB
  2. MDN
  3. indexedDB 標(biāo)準(zhǔn)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,317評(píng)論 2 89
  • ??支持離線 Web 應(yīng)用開(kāi)發(fā)是 HTML5 的另一個(gè)重點(diǎn)。 ??所謂離線 Web 應(yīng)用,就是在設(shè)備不能上網(wǎng)的情況...
    霜天曉閱讀 1,188評(píng)論 0 2
  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明AI閱讀 16,232評(píng)論 3 119
  • 近期有做一個(gè)圖片上傳的功能,官方文檔是這樣: 需求場(chǎng)景是上傳一個(gè)LOGO圖片并配以名稱,然后確定上傳 按照文檔,寫...
    道友試試重啟閱讀 10,264評(píng)論 9 2
  • 手工和實(shí)用藝術(shù)課程可以激發(fā)學(xué)生的創(chuàng)造力,此外,這些課程有意識(shí)地引導(dǎo)學(xué)生發(fā)展意志力,在此過(guò)程中建立起審美方面的自信。...
    何運(yùn)婷閱讀 245評(píng)論 0 0

友情鏈接更多精彩內(nèi)容