indexedDB,非關(guān)系型數(shù)據(jù)庫(kù)胳搞,W3C標(biāo)準(zhǔn)推薦
indexedDB是一種輕量級(jí)NOSQL數(shù)據(jù)庫(kù)卸例,是由瀏覽器自帶。相比Web Sql更加高效肌毅,包括索引筷转、事務(wù)處理和查詢功能。
在HTML5本地存儲(chǔ)中悬而,IndexedDB存儲(chǔ)的數(shù)據(jù)是最多的呜舒,不像webStorage的4M,IndexedDB存儲(chǔ)空間是無上限且永久的笨奠。
indexedDB支持度情況:
http://caniuse.com/#search=indexdb
對(duì)于ios10以前的版本有下面這一條說明:
Partial support in Safari & iOS 8 & 9 refers to seriously buggy behavior as well as complete lack of support in WebViews.
部分支持Safari和iOS 8和9指的是嚴(yán)重的錯(cuò)誤行為以及在WebViews完全缺乏支持袭蝗。
Ok,如果般婆,我們要用indexedDB到腥,對(duì)于ios10之前的版本,那到底有多嚴(yán)重呢蔚袍?
這個(gè)api里面有詳細(xì)的介紹乡范,其實(shí)基本的一些操作是可以用的。
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
在討論過indexedDB是否可用后,我們來正式學(xué)習(xí)一下它:
indexedDB特點(diǎn):
鍵值對(duì)存儲(chǔ):IndexedDB內(nèi)部采用對(duì)象倉(cāng)庫(kù)(object store)存放數(shù)據(jù)篓足。所有類型的數(shù)據(jù)都可以直接存入,包括JavaScript對(duì)象闰蚕。在對(duì)象倉(cāng)庫(kù)中栈拖,數(shù)據(jù)以“鍵值對(duì)”的形式保存,每一個(gè)數(shù)據(jù)都有對(duì)應(yīng)的鍵名没陡,鍵名是獨(dú)一無二的涩哟,不能有重復(fù),否則會(huì)拋出一個(gè)錯(cuò)誤盼玄。
異步:IndexedDB操作時(shí)不會(huì)鎖死瀏覽器贴彼,用戶依然可以進(jìn)行其他操作,這與localStorage形成對(duì)比埃儿,后者的操作是同步的器仗。異步設(shè)計(jì)是為了防止大量數(shù)據(jù)的讀寫,拖慢網(wǎng)頁(yè)的表現(xiàn)童番。
支持事務(wù):IndexedDB支持事務(wù)(transaction)精钮,這意味著一系列操作步驟之中,只要有一步失敗剃斧,整個(gè)事務(wù)就都取消轨香,數(shù)據(jù)庫(kù)回到事務(wù)發(fā)生之前的狀態(tài),不存在只改寫一部分?jǐn)?shù)據(jù)的情況幼东。
同域限制 :您也受到同域限制臂容,每一個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)創(chuàng)建該數(shù)據(jù)庫(kù)的域名。來自不同域名的網(wǎng)頁(yè)根蟹,只能訪問自身域名下的數(shù)據(jù)庫(kù)脓杉,而不能訪問其他域名下的數(shù)據(jù)庫(kù)。
儲(chǔ)存空間大:IndexedDB的儲(chǔ)存空間比localStorage大得多简逮,一般來說不少于250MB丽已。IE的儲(chǔ)存上限是250MB,Chrome和Opera是剩余空間的某個(gè)百分比买决,F(xiàn)irefox則沒有上限沛婴。
支持二進(jìn)制儲(chǔ)存: IndexedDB不僅可以儲(chǔ)存字符串,還可以儲(chǔ)存二進(jìn)制數(shù)據(jù)督赤。
indexedDB的主要方法:
1.創(chuàng)建數(shù)據(jù)庫(kù)window.indexedDB.open()
我們可以這樣創(chuàng)建或打開一個(gè)本地的數(shù)據(jù)庫(kù)對(duì)象
var openRequest =window.indexedDB.open(name, version);
var db;
openRequest.onupgradeneeded = function(e) {
console.log("Upgrading...");}
openRequest.onsuccess = function(e) {
console.log("Success!");
db = e.target.result;
}
openRequest.onerror = function(e) {
console.log("Error");
console.dir(e);
}
(1)第一次打開數(shù)據(jù)庫(kù)時(shí)嘁灯,會(huì)先觸發(fā)upgradeneeded事件,然后觸發(fā)success事件躲舌。
(2)open方法返回的是一個(gè)對(duì)象(IDBOpenDBRequest)丑婿,回調(diào)函數(shù)定義在這個(gè)對(duì)象上面。
(3)回調(diào)函數(shù)接受一個(gè)事件對(duì)象event作為參數(shù),它的target.result屬性就指向打開的IndexedDB數(shù)據(jù)庫(kù)羹奉。
如圖為chrom瀏覽器中的打開后的對(duì)象
2.indexedDB實(shí)例對(duì)象的方法—createObjectStore
createObjectStore方法用于創(chuàng)建存放數(shù)據(jù)的“對(duì)象倉(cāng)庫(kù)”(object store)秒旋,類似于傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)的表格。
db.createObjectStore("test", { keyPath: "email" });
db.createObjectStore("test2", { autoIncrement: true });
上面代碼中的keyPath屬性表示诀拭,所存入對(duì)象的email屬性用作每條記錄的鍵名(由于鍵名不能重復(fù)迁筛,所以存入之前必須保證數(shù)據(jù)的email屬性值都是不一樣的),默認(rèn)值為null耕挨;autoIncrement屬性表示细卧,是否使用自動(dòng)遞增的整數(shù)作為鍵名(第一個(gè)數(shù)據(jù)為1,第二個(gè)數(shù)據(jù)為2筒占,以此類推)贪庙,默認(rèn)為false。一般來說翰苫,keyPath和autoIncrement屬性只要使用一個(gè)就夠了止邮,如果兩個(gè)同時(shí)使用,表示鍵名為遞增的整數(shù)奏窑,且對(duì)象不得缺少指定屬性农尖。
if(!db.objectStoreNames.contains("firstOS")) {
db.createObjectStore(“firstOS”);
}
上面代碼先判斷某個(gè)“對(duì)象倉(cāng)庫(kù)”是否存在,如果不存在就創(chuàng)建該對(duì)象倉(cāng)庫(kù)良哲。
3.indexedDB實(shí)例對(duì)象的方法—transaction
transaction方法用于創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)事務(wù)盛卡。向數(shù)據(jù)庫(kù)添加數(shù)據(jù)之前,必須先創(chuàng)建數(shù)據(jù)庫(kù)務(wù)筑凫。
transaction方法返回一個(gè)事務(wù)對(duì)象滑沧,該對(duì)象的objectStore方法用于獲取指定的對(duì)象倉(cāng)庫(kù)。
var transaction = db.transaction(["firstOS"],"readwrite");
var store = transaction.objectStore("firstOS");
transaction方法接受兩個(gè)參數(shù):
第一個(gè)參數(shù)是一個(gè)數(shù)組巍实,里面是所涉及的對(duì)象倉(cāng)庫(kù)滓技,通常是只有一個(gè);
第二個(gè)參數(shù)是一個(gè)表示操作類型的字符串棚潦。readonly(只讀)和readwrite(讀寫)令漂。
transaction方法有三個(gè)事件,可以用來定義回調(diào)函數(shù)丸边。
abort: 事務(wù)中斷; complete: 事務(wù)完成; error: 事務(wù)出錯(cuò)叠必。
transaction.oncomplete = function(event) {
// some code
};
4.transaction對(duì)象—添加數(shù)據(jù): add方法
獲取對(duì)象倉(cāng)庫(kù)以后,就可以用add方法往里面添加數(shù)據(jù)了妹窖。
var transaction = db.transaction(["firstOS"],"readwrite");
var store = transaction.objectStore(“firstOS”);
var o = {p: 123};
var request = store.add(o,1);
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
request.onsuccess = function(e) {
console.log("數(shù)據(jù)添加成功纬朝!");
}
add方法的第一個(gè)參數(shù)是所要添加的數(shù)據(jù),第二個(gè)參數(shù)是這條數(shù)據(jù)對(duì)應(yīng)的鍵名(key)骄呼,上面代碼將對(duì)象o的鍵名設(shè)為1共苛。如果在創(chuàng)建數(shù)據(jù)倉(cāng)庫(kù)時(shí)判没,對(duì)鍵名做了設(shè)置,這里也可以不指定鍵名隅茎。上述操作也可以寫成鏈?zhǔn)降?/p>
下圖為一個(gè)學(xué)生對(duì)象添加到數(shù)據(jù)庫(kù)中在chrom瀏覽器中的存儲(chǔ)情況:
5.transaction對(duì)象—其他方法
(1)更新記錄:put方法澄峰,更新數(shù)據(jù)倉(cāng)庫(kù)中的對(duì)象
var o = { p:456 };
var request = store.put(o);
(2)讀取數(shù)據(jù):get方法,通過鍵值key獲取數(shù)據(jù)倉(cāng)庫(kù)中的對(duì)象
var request = store.get(key);
(3)刪除記錄:delete方法辟犀,通過鍵值key刪除數(shù)據(jù)倉(cāng)庫(kù)中的對(duì)象
var request = store.delete(key);
(4)清空數(shù)據(jù)倉(cāng)庫(kù):clear方法俏竞,刪除整個(gè)object store中的數(shù)據(jù)
var request = store.clear();
(5)遍歷數(shù)據(jù):openCursor方法,利用游標(biāo)遍歷數(shù)據(jù)踪蹬,下面詳細(xì)講一下這個(gè)方法
var request = store.openCursor();
6.transaction對(duì)象—遍歷數(shù)據(jù):openCursor方法
(1)利用游標(biāo)遍歷數(shù)據(jù)
var request = store.openCursor();
request.onsuccess = function(e) {
var cursor = e.target.result;
if(cursor && cursor !== null) {
console.log("Key", cursor.key);
console.dir("Data", cursor.value);
cursor.continue();
}else{
console.log(“遍歷結(jié)束");
}
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
(2)利用游標(biāo) 返回 或 刪除 指定的數(shù)據(jù)
var request = store.openCursor();
var data = [];
request.onsuccess = function(e) {
var cursor = e.target.result;
if(cursor && cursor !== null) {
if(cursor.[value.name](http://value.name) === ‘jack’){
data.push(cursor.value);
// 或者在此刪除數(shù)據(jù)
// cursor.delete();
}
cursor.continue();
}else{
console.log(“遍歷結(jié)束”)臣咖;
// 在此resolve(data);
}
request.onerror = function(e) {
console.log("Error",[e.target.error.name](http://e.target.error.name));
}
7.indexedDB實(shí)例對(duì)象的方法—索引createIndex與index
如圖一個(gè)學(xué)生對(duì)象跃捣,創(chuàng)建索引:
(1)createIndex方法用于創(chuàng)建索引。
var store = db.createObjectStore('student',{keyPath: “id”});
store.createIndex(‘emailIndex’,'email',{unique:true}); //郵箱
store.createIndex(‘classIndex’,'class',{unique:false}); //班級(jí)
store.createIndex(‘sexIndex','sex',{unique:false}); //性別
store.createIndex(‘classSexIndex’,['class','sex'],{unique:false}); //班級(jí)+性別
(2)createIndex方法接受三個(gè)參數(shù)夺蛇,第一個(gè)是索引名稱疚漆,第二個(gè)是建立索引的屬性名,第三個(gè)是參數(shù)對(duì)象刁赦,用來設(shè)置索引特性娶聘,unique表示索引所在的屬性是否有唯一值。
var index = store.index(“emailIndex");
index.get('jack').onsuccess=function(e){
var student=e.target.result;
console.log(student.id);
}
Index.get用來獲取唯一索引甚脉,如果不是唯一的丸升,就要用到游標(biāo)
8.IDBKeyRange對(duì)象—指定游標(biāo)范圍
索引的有用之處,還在于可以指定讀取數(shù)據(jù)的范圍牺氨。這需要用到瀏覽器原生的IDBKeyRange對(duì)象狡耻。
IDBKeyRange對(duì)象的作用是生成一個(gè)表示范圍的Range對(duì)象。生成方法有四種:
lowerBound方法:指定范圍的下限猴凹。
upperBound方法:指定范圍的上限夷狰。
bound方法:指定范圍的上下限。
only方法:指定范圍中只有一個(gè)值郊霎。
var r1 = IDBKeyRange.upperBound(x); All keys ≤ x
var r2 = IDBKeyRange.upperBound(x, true); All keys < x
var r3 = IDBKeyRange.lowerBound(y); All keys ≥ y
var r4 = IDBKeyRange.lowerBound(y, true); All keys > y
var r5 = IDBKeyRange.bound(x, y); All keys ≥ x && ≤ y
var r6 = IDBKeyRange.bound(x, y, true, true); All keys > x &&< y
var r7 = IDBKeyRange.bound(x, y, true, false); All keys > x && ≤ y
var r8 = IDBKeyRange.bound(x, y, false, true); All keys ≥ x &&< y
var r9 = IDBKeyRange.only(z); // The key = z
如下圖所示的方法沼头,便可
利用索引查詢與特定條件匹配的所有記錄
利用sexIndex查找所有男同學(xué)的信息
getIndex(‘student’,’sexIndex’,’male’);
利用classSexIndex查找所有二年六班的女同學(xué)的信息
getIndex(‘student’,’classSexIndex’,[‘二年六班’,’female’]);
由此可見书劝,在項(xiàng)目中使用进倍,對(duì)于數(shù)據(jù)庫(kù)的封裝飾很有必要的,
下面我在項(xiàng)目中封裝的indexedDB的一些部分购对,僅供參考:
dbModule.factory('iDbService',["$http", "$q", function ($http,$q) {
var myDB = {
name : 'localIndexDB',
version : 1,
db : null
};
var openDB = function (name, version, stores) {
console.log('openDB');
var d = $q.defer();
var _name = [myDB.name](http://myDB.name) || name;
var _version = myDB.version || version;
//打開數(shù)據(jù)庫(kù)
var result = window.indexedDB.open(_name,_version);
//錯(cuò)誤
result.onerror = function(e){
console.log("Open DB Error!");
d.reject("error");
};
//正確打開
result.onsuccess = function(e){
myDB.db = e.target.result ;
console.log('success');
d.resolve("success");
};
//數(shù)據(jù)庫(kù)版本變更
result.onupgradeneeded = function(e){
myDB.db = e.target.result ;
if(!myDB.db.objectStoreNames.contains('users')){
myDB.db.createObjectStore('users',{keyPath: "id"});
}
if(!myDB.db.objectStoreNames.contains('infos')){
myDB.db.createObjectStore('infos',{autoIncrement: true});
}
if(!myDB.db.objectStoreNames.contains('problem')){
var store = myDB.db.createObjectStore('problem',{keyPath: "fqid"}); // 建數(shù)據(jù)倉(cāng)庫(kù)object store
store.createIndex('minTaskIndex',['fmid','fstate'],{unique:false}); // 創(chuàng)建索引
}
console.log('upgradeneeded');
d.resolve("upgradeneeded");
};
return d.promise;
};
openDB();
// 通過key查詢數(shù)據(jù)
var get = function (storeName,key) {
var d = $q.defer();//promise
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var result = store.get(key);
result.onsuccess = function (e) {
var data = e.target.result;
console.log('indexDb 一條數(shù)據(jù)查詢結(jié)果為:');
console.log(data);
d.resolve(data);
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 查詢一個(gè)object store的所有數(shù)據(jù)
var getAll = function(storeName){
var d = $q.defer();//promise
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var result = store.openCursor(); // 打開一個(gè)游標(biāo)
var data = [];
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
var problem = cursor.value;
var jsonStr = JSON.stringify(problem);
data.push(problem);
cursor.continue();
}else {
console.log('indexDb 一張表數(shù)據(jù)查詢結(jié)果為:');
// console.log(data);
d.resolve(data);
}
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 根據(jù)索引,查詢與特定條件匹配的所有記錄
var getIndex = function(storeName,indexName,params){
var d = $q.defer();//promise
console.log('索引查詢的params');
console.log(params);
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readonly');
var store = transaction.objectStore(storeName);
var index = store.index(indexName);
var range = IDBKeyRange.only(params);
var result = index.openCursor(range); // 打開一個(gè)游標(biāo)
var data = [];
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
var problem = cursor.value;
var jsonStr = JSON.stringify(problem);
data.push(problem);
cursor.continue();
}else {
console.log('indexDb' + storeName + '表中' + indexName + '索引,數(shù)據(jù)查詢結(jié)果為:');
// console.log(data);
d.resolve(data);
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
// 新增數(shù)據(jù)
var add = function (storeName,value) {
var d = $q.defer();
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.add(value);
result.onsuccess = function(e) {
console.log("create note success!");
console.log(myDB);
d.resolve();
};
result.onerror = function(e) {
console.log("can't create database,error:" + result.error);
d.reject();
};
return d.promise;
};
// 插入更新數(shù)據(jù)
var put = function (storeName,value) {
var d = $q.defer();
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.put(value);
result.onsuccess = function(e) {
console.log("create note success!");
d.resolve();
};
result.onerror = function(e) {
console.log("can't create database,error:" + result.error);
d.reject();
};
return d.promise;
};
// 通過key刪除對(duì)象
var remove = function (storeName,key) {
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.delete(key);
result.onsuccess = function (e) {
console.log(e);
};
result.onerror = function (e) {
console.log(e);
};
};
// clear方法可以清空object store
var clear = function (storeName) {
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.clear();
result.onsuccess = function (e) {
console.log(e);
};
result.onerror = function (e) {
console.log(e);
};
};
// 刪除與特定條件匹配的所有記錄
var removeData = function(storeName,arg,value){
var d = $q.defer();//promise
console.log('刪除匹配值的arg+value');
console.log(arg);
console.log(value);
var _db = myDB.db;
var transaction = _db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
var result = store.openCursor(); // 打開一個(gè)游標(biāo)
result.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor && cursor !== null) {
if(cursor.value[arg[0]] === value[0] && cursor.value[arg[1]] === value[1]) {
var request = cursor.delete();
request.onsuccess = function() {
console.log('Deleted this cursor data.');
};
} else {
console.log('131231312');
}
cursor.continue();
}else {
console.log('Entries displayed.');
d.resolve();
}
};
result.onerror = function (e) {
d.reject();
};
return d.promise;
};
return {
openDB: openDB,
get: get,
getAll: getAll,
getIndex: getIndex,
add: add,
put: put,
remove: remove,
clear: clear,
removeData: removeData
};
}]);