IndexedDB是在瀏覽器中保存結構化數據的一種數據庫,為了替換WebSQL(標准已廢棄,但被廣泛支持)而出現。IndexedDB使用NoSQL的形式來操作數據庫,保存和讀取是JavaScript對象,同時還支持查詢及搜索。
下面由5個方面講述:
1. 數據庫初始化
2. 對象存儲空間(ObjectStore)
3. 事務
4. 游標查詢
5. 索引
數據庫初始化
IndexedDB保存的是對象,而不是使用表保存數據。打開數據庫使用indexDB.open方法,這方法有兩個參數,第一個是數據庫名稱,第二個是數據版本號。
PS:IndexedDB的操作完全是異步進行的,每一次IndexedDB操作,都需要注冊onerror或onsuccess事件處理程序。
var DB_NAME = 'DEMO'; var DB_VERSION = 2; //使用正整數,別用浮點型 var db; function initDb() { console.debug("initDb ..."); var req = indexedDB.open(DB_NAME, DB_VERSION); req.onsuccess = function (evt) { db = evt.target.result; console.debug("initDb opened"); }; req.onerror = function (evt) { console.error("initDb error:", evt.target.errorCode || evt.target.error); }; //增加數據庫版本號時,會觸發onupgradeneeded事件(會在onsuccess之前被調用) req.onupgradeneeded = function (evt) { console.debug("initDb.onupgradeneeded"); }; }
PS:這里要注意的是,數據庫版本只會有最新一個,不會同時存在兩個版本的同名數據庫。
對象存儲空間(ObjectStore)
對象存儲空間(ObjectStore)可以想象成關系數據庫的表,在初始化DB觸發onupgradeneeded時,創建ObjectStore。使用createObjectStore方法,第一個參數是對象名,第二個參數是對象屬性,一般是設置keyPath(作為鍵使用)。
req.onupgradeneeded = function (evt) { console.debug("initDb.onupgradeneeded"); var db = evt.currentTarget.result; //ObjectStore必須在onupgradeneeded里創建,其他地方將會創建失敗 var usersStore = db.createObjectStore("users", { keyPath : "id" }); };
效果如下:


事務
所有讀取或修改數據的操作,都要通過事務來完成。創建事務使用transaction方法,第一個參數是需要訪問的ObjectStore,第二個參數是訪問模式(readwrite、readonly,默認是只讀)。
添加數據
function addData(){ var users = [{ id : '001', name : '劉亦菲', age : 18 },{ id : '002', name : '楊冪', age : 19 }]; var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var i = 0, len = users.length; while(i < len){ store.add(users[i++]); } }
獲取數據
function getData(){ var tx = db.transaction("users"); var store = tx.objectStore("users"); var req = store.get("001"); req.onsuccess = function (evt) { var res = evt.target.result; console.debug(res); }; req.onerror = function (evt) { console.error("getData error:", evt.target.errorCode || evt.target.error); }; }
修改數據
function updateData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.put({ id : '001', name : '劉亦菲-小龍女', age : 18 }); req.onsuccess = function (evt) { console.debug("updateData success"); }; req.onerror = function (evt) { console.error("updateData error:", evt.target.errorCode || evt.target.error); }; }
刪除數據
function delData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.delete("001"); req.onsuccess = function (evt) { console.debug("delData success"); }; req.onerror = function (evt) { console.error("delData error:", evt.target.errorCode || evt.target.error); }; }
清空數據
function clearData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.clear(); req.onsuccess = function (evt) { console.debug("clearData success"); }; req.onerror = function (evt) { console.error("clearData error:", evt.target.errorCode || evt.target.error); }; }
游標查詢
使用事務可以直接通過鍵檢索單個對象,而需要檢索多個對象時候就需要使用游標。游標是指向結果集的指針,不提前收集結果。游標指針會先指向結果中的第一項,在接到查找下一項指令時,才會指向下一項。
function openCursor(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.openCursor(); req.onsuccess = function (evt) { var cursor = evt.target.result; if(cursor){ //必要檢查 var value = cursor.value; console.log(value); if(value.name == '楊冪'){ value.age = 16; cursor.update(value); //修改數據(必須是讀寫模式) } if(value.name == '柳岩'){ cursor.delete(); //刪除當前項 } cursor.continue(); //移動到下一項 } }; req.onerror = function (evt) { console.error("openCursor error:", evt.target.errorCode || evt.target.error); }; }
這里有幾點要注意:
1. 如果需要修改或刪除數據,就需要打開成讀寫模式。
2. cursor的非空校驗是必要的。
3. 修改或刪除的操作也是有onsuccess和onerror的,只是在示例中沒有寫出來。
4. 調用continue才會移動到下一項
另外可以設置游標的鍵范圍和游標的方向,即打開openCursor方法時可以傳這兩個參數(openCursor(鍵范圍,方向)),第一個參數是object類型,第二個參數是字符串類型。
游標鍵范圍
鍵范圍由IDBKeyRange的實例表示。
IDBKeyRange.only('001'); //只想要鍵為001的結果
IDBKeyRange.lowerBound('002'); //從鍵為002開始,到最后
IDBKeyRange.lowerBound('002', true); //從鍵為002開始,但忽略002,到最后
IDBKeyRange.upperBound('002'); //從頭開始,到鍵為002為止
IDBKeyRange.upperBound('002', true); //從頭開始,到鍵為002為止,但忽略002
IDBKeyRange.bound('001', '005'); //從001開始,到為005為止
IDBKeyRange.bound('001', '005', true, true); //從001開始,到為005為止,但忽略001、005
游標方向
next : 從第一項到最后一項(默認)
prev : 從最后一項到第一項
索引
當需要使用其他屬性(非主鍵)獲取數據時,就要預先創建索引,然后使用索引獲取數據。
創建索引(在數據庫初始化onupgradeneeded事件時)
第一個參數是索引名字,第二個參數是索引的屬性的名字,第三個是一個options對象。一般是指定unique,設置索引是否唯一。
usersStore.createIndex("name", "name", { unique : false });
索引獲取數據
function indexGetData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var index = store.index("name"); var req = index.get("楊冪") req.onsuccess = function (evt) { console.debug("indexGet success" , evt.target.result); }; req.onerror = function (evt) { console.error("indexGet error:", evt.target.errorCode || evt.target.error); }; } function indexOpenCursor(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var index = store.index("name"); var req = index.openCursor(); req.onsuccess = function (evt) { var cursor = evt.target.result; if(cursor){ //必要檢查 var value = cursor.value; console.log(value); cursor.continue(); //移動到下一項 } }; req.onerror = function (evt) { console.error("openCursor error:", evt.target.errorCode || evt.target.error); }; }
PS:索引用法跟普通取值和游標取值一樣
對象存儲所有索引
function indexNames(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var indexNames = store.indexNames; var index, i = 0, len = indexNames.length; while(i < len){ index = store.index(indexNames[i++]); console.log(index); } }
瀏覽器支持情況

PS:圖表來源->http://caniuse.com/#feat=indexeddb
總結
在使用IndexedDB時候,有人可能會拿WebSQL來比較,然后發現IndexedDB不能做表連接(因為根本沒有這東西),也就是要查出一個數據,可能得分幾次進行。
例如學生、課程、分數三個表數據,想查出某個學生的課程成績,就得三個表連接,WebSQL分分鍾信手拈來。但是如果你用IndexedDB,就得分三次查找,先拿出那個學生,再拿出課程,然后再拿成績。
這樣看起來IndexedDB很蠢,這樣就進入誤區了,你為什么要這么去存你要展示的數據,NoSql就用NoSql東西,就直接以一個對象存學生成績,一次查找就行了。
示例下載:http://files.cnblogs.com/files/lovesong/IndexedDBdemo.zip
本文為原創文章,轉載請保留原出處,方便溯源,如有錯誤地方,謝謝指正。
