indexeddb:瀏覽器中的資料庫

  • 2019 年 10 月 21 日
  • 筆記

隨著瀏覽器功能的不斷加強,越來越多的網站開始考慮將大量的數據存儲在客戶端。這樣的考慮是為了直接從本地獲取數據,減少從伺服器獲取數據耗費的網路資源。

原有的瀏覽器數據存儲方案都不適合存儲大量數據。Cookie的大小不超過4KB,且每次請求都會發送會伺服器;WebStorage(LocalStorage、SessionStorage)的大小在2.5MB到10MB之間(各家瀏覽器不同,一般可以認為是5MB左右),而且不提供搜索功能,也不能建立自定義的索引。因此IndexedDB就誕生了,是H5提供的一種新的瀏覽器存儲解決方案。

什麼IndexedDB

通俗地說,IndexedDB就是瀏覽器提供的一個本地資料庫,可以被網頁腳本創建和操作。IndexedDB允許存儲大量的數據,同時提供查找借口,還能創建索引。而這些都是Cookie和WebStorage所不具備的。就資料庫而言,IndexedDB不屬於關係型資料庫(不支援SQL查詢語句),更接近與NoSQL資料庫。

Indexed具有以下特點:

1.鍵值對存儲。IndexedDB內部採用對象倉庫(Object Store)存放數據。所有類型的數據都可以直接存入,包括JavaScript對象。在對象倉庫中,數據以鍵值對的形式保存,每一個數據記錄都有對應的主鍵,且這個主鍵是唯一的,一旦重複就會拋出錯誤。

2.非同步。IndexedDB操作的時候不會鎖死瀏覽器(執行緒不會掛起),用戶依然可以進行其他操作。這與LocalStorage形成對比,後者的操作是同步的(執行緒會掛起)。非同步的設計是為了防止大量數據的讀寫拖慢網頁的表現性能。

3.支援事務。IndexedDB是支援事務(Transaction)的。這就意味著,一系列的操作中只要有一個步驟失敗,整個事務就會取消,資料庫也會回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況。

4.同源策略。IndexedDB受瀏覽器同源策略的限制,每一個資料庫都對應創建它的域名。網頁只能訪問自身域名下的資料庫,不能訪問跨域的資料庫。

5.存儲空間大。IndexedDB的存儲空間比WebStorage要大得多,一般來說不小於250MB,甚至沒有上限。

6.支援二進位存儲。IndexedDB不僅可以存儲字元串,還可以存儲二進位數據(ArrayBuffer和Blob對象)。

Indexed中的一些主要概念

IndexedDB是一個比較複雜的API,涉及到不少概念。它把不同的實體抽象成一個個對象介面。學習這個API實際上就是學習它提供的各種對象介面。

資料庫 IDBDatabase對象
對象倉庫 IDBObjectStore對象
索引 IDBIndex對象
事務 IDBTransaction對象
操作請求 IDBRequest對象
指針 IDBCursor對象
主鍵集合 IDBKeyRange對象

下面對一些主要概念進行說明:

1.資料庫。資料庫是一系列相關數據的容器。每個域名都可以新建任意多個資料庫。另外,在IndexedDB資料庫中有版本的概念,在同一時刻只能有一個版本的資料庫存在。如果要修改資料庫的結構(新增表或刪除表、索引和主鍵),只能通過升級資料庫版本完成。

2.對象倉庫。每個資料庫包含若干個對象倉庫,類似於關係型資料庫的表格。

3.數據記錄。對象倉庫保存的是數據記錄。每條記錄類似於關係型資料庫的行,但是只有主鍵和數據體兩個部分。主鍵用來建立默認的索引,必須是不同的,否則會報錯。主鍵可以是實數據記錄裡面的一個屬性,也可以另外指定為一個遞增的整數編號。數據體可以是任意的數據類型,不限於對象。

4.索引。為了加速數據的索引,可以在對象倉庫裡面為不同的屬性建立索引。

5.事務。數據記錄的讀寫和刪改,都要通過事務來完成。事務對象提供了error、abort和complete三個事件用來監聽操作的結果。

IndexedDB的常用操作

這裡給出IndexedDB資料庫常用操作的簡單示例。

1.打開資料庫

使用IndexedDB的第一步是打開資料庫,使用indexed.open()方法。

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

這個方法接受兩個參數,第一個參數是字元串,表示資料庫名,如果指定的資料庫不存在,就會新建一個資料庫。第二個參數是整數,表示資料庫的版本,如果省略,就會打開現有的資料庫版本。在新建資料庫的時候,默認版本為1。

這個方法會返回一個IDBRequest對象。這個對象可以通過onerror、onsuccess和onupgradeneeded事件處理打開資料庫的操作結果。

onerror事件,表示打開資料庫失敗。

request.onerror = function (event) {      console.log('資料庫打開報錯');  };

onsuccess事件,表示打開資料庫成功。

request.onsuccess = function (event) {      var db = request.result;      console.log('資料庫打開成功', db);  };

onupgradeneeded事件,如果指定的版本號大於資料庫的實際版本號,就會發生資料庫升級事件。

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

2.新建資料庫

新建資料庫與打開資料庫是同一個操作,如果指定的資料庫不存在,就會新建一個資料庫。不同之處在於,打開資料庫是觸發的onsuccess事件,而新建資料庫是觸發的onupgradeneeded事件,因為這時候版本是從無到有。

通常新建資料庫之後的第一件事是新建對象倉庫(新建表)。

var db;  request.onupgradeneeded = function(event) {      db = event.target.result;      var objectStore = db.createObjectStore('yanggb', {keyPath: 'id'});  }

上面的程式碼中,在資料庫新建成功後會新增一張叫做yanggb的表,主鍵是id。

更好的寫法是,先判斷以下表格是否存在,如果不存在的話再新增。

var db;  request.onupgradeneeded = function(event) {      db = event.target.result;      var objectStore;      if (!db.objectStoreNames.contains('yanggb')) {          objectStore = db.createObjectStore('yanggb', {keyPath: 'id'});      }  }

主鍵(key)是默認建立索引的屬性。如果數據記錄沒有適合作為主鍵的屬性,也可以讓IndexedDB自動生成主鍵,下面的程式碼中就指定了主鍵為一個遞增的整數。

var objectStore = db.createObjectStore('yanggb', {autoIncrement: true});

再新建了對象倉庫之後,下一步就可以新建索引。

var db;  request.onupgradeneeded = function(event) {      db = event.target.result;      var objectStore;      if (!db.objectStoreNames.contains('yanggb')) {          objectStore = db.createObjectStore('yanggb', {keyPath: 'id'});          objectStore.createIndex('index_book', 'book', {unique: false});          objectStore.createIndex('index_email', 'email', {unique: true});      }  }

IDBObject.createIndex()的三個參數分別為索引名稱、索引所在的屬性和配置對象(說明該屬性能否包含重複的值)。

3.新增數據

新增數據指的是向對象倉庫中寫入數據記錄,需要通過事務對象來完成。

function addRecord() {      var request = db.transaction(['yanggb'], 'readwrite')          .objectStore('yanggb')          .add({id: 1, book: '十一種孤獨', email: '[email protected]'});        request.onsuccess = function (event) {          console.log('數據寫入成功!');      };        request.onerror = function (event) {          console.log('數據寫入失敗!');      }  }

在上面的程式碼中,寫入數據需要新建一個事務。新建的時候必須要指定表名和操作模式(只讀read或者讀寫readwrite)。新建事務之後就可以通過IDBTransaction.objectStore(name)方法拿到IDBObjectStore對象,然後通過表格對象的add()方法向表格中寫入一行記錄。

這個寫入操作是一個非同步操作,通過監聽連接對象的success事件和error事件可以了解是否寫入成功或做相應的後續操作。

4.讀取數據

讀取數據同樣也是通過事務對象來完成的。

function getRecord() {      var transaction = db.transaction(['yanggb']);      var objectStore = transaction.objectStore('yanggb');      var request = objectStore.get(1);        request.onerror = function(event) {          console.log('事務失敗');      };        request.onsuccess = function(event) {          if (request.result) {              console.log('book: ' + request.result.book);              console.log('email: ' + request.result.email);          } else {              console.log('未獲得數據記錄!');          }     };  }

IDBObjectStore.get()方法用於讀取數據,接收一個參數,參數是主鍵的值。

5.遍曆數據

如果要遍曆數據倉庫中的所有數據記錄,要使用指針對象IDBCursor。

function getRecords() {      var objectStore = db.transaction('yanggb').objectStore('yanggb');        objectStore.openCursor().onsuccess = function (event) {          var cursor = event.target.result;            if (cursor) {              console.log('id: ' + cursor.key);              console.log('book: ' + cursor.value.book);              console.log('email: ' + cursor.value.email);              cursor.continue();          } else {              console.log('沒有更多數據了!');          }      };  }

這裡,獲取數據記錄的操作是在IDBCursor對象中的onsuccess監聽事件中完成的。

6.更新數據

更新數據要使用IDBObjectStore對象的put()方法。

function updateRecord() {      var request = db.transaction(['yanggb'], 'readwrite')          .objectStore('yanggb')          .put({id: 1, book: '追風箏的任靜', email: '[email protected]'});        request.onsuccess = function (event) {          console.log('數據更新成功!');      };        request.onerror = function (event) {          console.log('數據更新失敗');      }  }

上面的程式碼中,IDBObject.put()方法會自動更新主鍵為1的記錄。

7.刪除數據

刪除數據要使用IDBObjectStore對象的delete()方法。

function remove() {      var request = db.transaction(['yanggb'], 'readwrite')          .objectStore('yanggb')          .delete(1);        request.onsuccess = function (event) {          console.log('數據刪除成功');      };  }

IDBObjectStore.delete()方法用於刪除數據,接受一個參數,參數值為主鍵的值。

8.使用索引

索引的意義在於可以快速通過任意欄位拿到數據記錄。如果不建立索引的話,默認只能搜索主鍵(從主鍵取值)。在上面新建表格的時候已經對book欄位建立了索引,於是可以通過book欄位找到對應的數據記錄。

function searchRecordByIndexOfBook() {      var transaction = db.transaction(['yanggb'], 'readonly');      var store = transaction.objectStore('yanggb');      var index = store.index('book');      var request = index.get('追風箏的任靜');        request.onsuccess = function (e) {          var result = e.target.result;          if (result) {              console.log('找到對應數據!');          } else {              console.log('未找到對應數據!');          }      }  }

如果想要了解更多的話,可以查看官網的API:https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

 

“我唱得不夠動人你別皺眉。”