IndexedDB 教程
IndexedDB 是一個基於 JavaScript 的面向對象的事務型數據庫。有了 LocalStorage 和 Cookies,為什么還要推出 indexedDB 呢?其實對於在瀏覽器里存儲數據,可以使用 cookies 或 LocalStorage,但它們都是比較簡單的技術,而 IndexedDB 提供了類似數據庫風格的數據存儲和使用方式。
LocalStorage 與 IndexedDB 區別也適用場景
LocalStorage 是用 key-value 鍵值模式存儲數據,它存儲的數據都是字符串形式。如果你想讓 LocalStorage存儲對象,你需要借助 JSON.stringify()能將對象變成字符串形式,再用 JSON.parse()將字符串還原成對象,就是專門為小數量數據設計的,所以它的 api 設計為同步的。
IndexedDB 很適合存儲大量數據,它的 API 是異步調用的。IndexedDB 使用索引存儲數據,各種數據庫操作放在事務中執行。IndexedDB 甚至還支持簡單的數據類型。IndexedDB 比 localstorage 強大得多,但它的 API 也相對復雜。對於簡單的數據,你應該繼續使用 localstorage,但當你希望存儲大量數據時,IndexedDB 會明顯的更適合,IndexedDB 能提供你更為復雜的查詢數據的方式。
indexedDB 的特性
- 對象倉庫
有了數據庫后我們自然希望創建一個表用來存儲數據,但 indexedDB 中沒有表的概念,而是 objectStore,一個數據庫中可以包含多個 objectStore,objectStore 是一個靈活的數據結構,可以存放多種類型數據。也就是說一個 objectStore 相當於一張表,里面存儲的每條數據和一個鍵相關聯。我們可以使用每條記錄中的某個指定字段作為鍵值(keyPath),也可以使用自動生成的遞增數字作為鍵值(keyGenerator),也可以不指定。選擇鍵的類型不同,objectStore 可以存儲的數據結構也有差異。
- 事務性
在 indexedDB 中,每一個對數據庫操作是在一個事務的上下文中執行的。事務范圍一次影響一個或多個 object stores,你通過傳入一個 object store 名字的數組到創建事務范圍的函數來定義。例如:db.transaction(storeName, 'readwrite'),創建事務的第二個參數是事務模式。當請求一個事務時,必須決定是按照只讀還是讀寫模式請求訪問。
- 基於請求
對 indexedDB 數據庫的每次操作,描述為通過一個請求打開數據庫,訪問一個 object store,再繼續。IndexedDB API 天生是基於請求的,這也是 API 異步本性指示。對於你在數據庫執行的每次操作,你必須首先為這個操作創建一個請求。當請求完成,你可以響應由請求結果產生的事件和錯誤。
- 異步
在 IndexedDB 大部分操作並不是我們常用的調用方法,返回結果的模式,而是請求—響應的模式,所謂異步 API 是指並不是這條指令執行完畢,我們就可以使用 request.result 來獲取 indexedDB 對象了,就像使用 ajax 一樣,語句執行完並不代表已經獲取到了對象,所以我們一般在其回調函數中處理。
幾個概念
-
- IDBFactory.open 方法發送一個打開或者創建一個數據庫的請求
- IDBFactory.deleteDatabase 方法: 發送一個刪除數據庫的請求
-
- IDBDatabase.close 方法關閉數據庫。
- IDBDatabase.createObjectStore 方法創建 store,相當於表
- IDBDatabase.transaction 開啟一個事務。
-
IDBRequest:機會是所有 indexedDB 操作的返回值,indexedDB 操作請求
- IDBRequest.result 結果
- IDBRequest.onerror 異常事件
- IDBRequest.onsuccess 成功的事件
快速入門 demo
- 打開數據庫實例。
var db; // 全局的indexedDB數據庫實例。 //1\. 獲取IDBFactory接口實例(文檔地址: 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\. 通過IDBFactory接口的open方法打開一個indexedDB的數據庫實例 // 第一個參數: 數據庫的名字,第二個參數:數據庫的版本。返回值是一個:IDBRequest實例,此實例有onerror和onsuccess事件。 var IDBOpenDBRequest = indexedDB.open('demoDB', 1); // 3\. 對打開數據庫的事件進行處理 // 打開數據庫成功后,自動調用onsuccess事件回調。 IDBOpenDBRequest.onsuccess = function(e) {}; // 打開數據庫失敗 IDBOpenDBRequest.onerror = function(e) { console.log(e.currentTarget.error.message); }; // 第一次打開成功后或者版本有變化自動執行以下事件:一般用於初始化數據庫。 IDBOpenDBRequest.onupgradeneeded = function(e) { db = e.target.result; // 獲取到 demoDB對應的 IDBDatabase實例,也就是我們的數據庫。 if (!db.objectStoreNames.contains(personStore)) { //如果表格不存在,創建一個新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會返回一個對象(objectStore) // objectStore就相當於數據庫中的一張表。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('數據庫版本更改為: ' + dbVersion); };
- 數據庫的 objectStore 添加
indexedDB 的增刪改查的操作需要放到一個事務中進行(推薦)
// 創建一個事務,類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction var transaction = db.transaction(personStore, 'readwrite'); // 通過事務來獲取IDBObjectStore var store = transaction.objectStore(personStore); // 往store表中添加數據 var addPersonRequest = store.add({ name: '老馬', phone: '189111833', address: 'aicoder.com' }); // 監聽添加成功事件 addPersonRequest.onsuccess = function(e) { console.log(e.target.result); // 打印添加成功數據的 主鍵(id) }; // 監聽失敗事件 addPersonRequest.onerror = function(e) { console.log(e.target.error); };
- 數據庫的 objectStore 修改
// 創建一個事務,類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction var transaction = db.transaction(personStore, 'readwrite'); // 通過事務來獲取IDBObjectStore var store = transaction.objectStore(personStore); var person = { id: 6, name: 'lama', phone: '515154084', address: 'aicoder.com' }; // 修改或者添加數據。 第一參數是要修改的數據,第二個參數是主鍵(可省略) var updatePersonRequest = store.get(6); // 監聽添加成功事件 updatePersonRequest.onsuccess = function(e) { // var p = e.target.result; // 要修改的原對象 store.put(person); }; // 監聽失敗事件 updatePersonRequest.onerror = function(e) { console.log(e.target.error); };
- 數據庫的 objectStore 刪除
// 創建一個事務,類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction var transaction = db.transaction(personStore, 'readwrite'); // 通過事務來獲取IDBObjectStore var store = transaction.objectStore(personStore); store.delete(6).onsuccess = function(e) { console.log(刪除成功!) };
- 根據 id 獲取數據
// 創建一個事務,類型:IDBTransaction,文檔地址: https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction var transaction = db.transaction(personStore, 'readwrite'); // 通過事務來獲取IDBObjectStore var store = transaction.objectStore(personStore); store.get(6).onsuccess = function(e) { console.log(刪除成功!) };
- 數據庫的 objectStore 游標查詢
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(); // 游標繼續往下 搜索,重復觸發 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">