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。
“我唱得不夠動人你別皺眉。”