IndexedDB 教程

IndexedDB 教程

IndexedDB 是一個基于 JavaScript 的面向對象的事務型數(shù)據(jù)庫。有了 LocalStorageCookies,為什么還要推出 indexedDB 呢?其實對于在瀏覽器里存儲數(shù)據(jù),可以使用 cookiesLocalStorage,但它們都是比較簡單的技術,而 IndexedDB 提供了類似數(shù)據(jù)庫風格的數(shù)據(jù)存儲和使用方式。

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

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

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

indexedDB 的特性

  • 對象倉庫

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

  • 事務性

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

  • 基于請求

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

  • 異步

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

幾個概念

快速入門 demo

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

//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的數(shù)據(jù)庫實例
// 第一個參數(shù): 數(shù)據(jù)庫的名字,第二個參數(shù):數(shù)據(jù)庫的版本。返回值是一個:IDBRequest實例,此實例有onerror和onsuccess事件。
var IDBOpenDBRequest = indexedDB.open('demoDB', 1);

// 3\. 對打開數(shù)據(jù)庫的事件進行處理

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

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

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

  if (!db.objectStoreNames.contains(personStore)) {
    //如果表格不存在,創(chuàng)建一個新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會返回一個對象(objectStore)
    // objectStore就相當于數(shù)據(jù)庫中的一張表。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ù)庫版本更改為: ' + dbVersion);
};
  • 數(shù)據(jù)庫的 objectStore 添加

indexedDB 的增刪改查的操作需要放到一個事務中進行(推薦)

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

// 通過事務來獲取IDBObjectStore
var store = transaction.objectStore(personStore);

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

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

// 監(jiān)聽失敗事件
addPersonRequest.onerror = function(e) {
  console.log(e.target.error);
};
  • 數(shù)據(jù)庫的 objectStore 修改
// 創(chuàng)建一個事務,類型: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'
};

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

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

// 監(jiān)聽失敗事件
updatePersonRequest.onerror = function(e) {
  console.log(e.target.error);
};
  • 數(shù)據(jù)庫的 objectStore 刪除
// 創(chuàng)建一個事務,類型: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(刪除成功!)
};
  • 根據(jù) id 獲取數(shù)據(jù)
// 創(chuàng)建一個事務,類型: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(刪除成功!)
};
  • 數(shù)據(jù)庫的 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(); // 游標繼續(xù)往下 搜索,重復觸發(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對象,兼容各種瀏覽器
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
      console.log("你的瀏覽器不支持IndexedDB");
    }

    openIndexedDB(loadTableData);

    // 配合游標遍歷表中數(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(); // 游標繼續(xù)往下 搜索,重復觸發(fā) onsuccess方法,如果到到返回null
        }
      }
    }

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

      // 通過事務來獲取store
      var store = transaction.objectStore(personStore);

      // 刪除請求
      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)建一個事務
      var transaction = db.transaction(personStore, 'readwrite');

      // 通過事務來獲取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);
      }
    }

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

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

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

      // 打開成功后,如果版本有變化自動執(zhí)行以下事件
      request.onupgradeneeded = function (e) {
        var db = e.target.result;
        if (!db.objectStoreNames.contains(personStore)) {
          console.log("我需要創(chuàng)建一個新的存儲對象");
          //如果表格不存在,創(chuàng)建一個新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會返回一個對象(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ù)庫版本更改為: ' + dbVersion);
      };
    }
  </script>
</body>
</html>

另外一個封裝的案例

<!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 本地存儲——IndexedDB
  2. MDN
  3. indexedDB 標準
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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

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