前言
最近在做的項(xiàng)目上有離線功能谦屑,需要將大量的數(shù)據(jù)存儲(chǔ)在前端谤碳。結(jié)合項(xiàng)目實(shí)際需求最后決定使用indexedDB來進(jìn)行存儲(chǔ)材泄。以下是我對(duì)indexedDB的使用以及一些踩過的坑進(jìn)行總結(jié)悠鞍。
1.基本使用
1.1 打開/新建數(shù)據(jù)庫(kù)
使用indexedDB.open()方法
var request = window.indexedDB.open(databaseName, version);
databaseName:數(shù)據(jù)庫(kù)名稱染簇,如果指定數(shù)據(jù)庫(kù)不存在,則會(huì)新建該名稱的數(shù)據(jù)庫(kù)
version:整數(shù)参滴,表示數(shù)據(jù)庫(kù)的版本號(hào)。打開已有數(shù)據(jù)庫(kù)默認(rèn)為當(dāng)前版本锻弓。新建數(shù)據(jù)庫(kù)時(shí)砾赔,默認(rèn)版本號(hào)為1。
indexedDB.open()方法返回一個(gè) IDBRequest 對(duì)象青灼。這個(gè)對(duì)象通過以下三種事件處理打開數(shù)據(jù)庫(kù)的操作結(jié)果暴心。
(1)onerror:打開數(shù)據(jù)庫(kù)失敗
request.onerror = function (event) {
console.log('數(shù)據(jù)庫(kù)打開報(bào)錯(cuò)');
};
(2)success:打開數(shù)據(jù)庫(kù)成功
var db;
request.onsuccess = function (event) {
db = request.result;
console.log('數(shù)據(jù)庫(kù)打開成功');
};
(3) upgradeneeded:數(shù)據(jù)庫(kù)版本升級(jí)或創(chuàng)建數(shù)據(jù)庫(kù)時(shí)觸發(fā),在該事件中創(chuàng)建數(shù)據(jù)表
var db;
request.onupgradeneeded = function (event) {
db = event.target.result;
if (!db.objecttables.contains('table')) { //判斷數(shù)據(jù)庫(kù)中是否已經(jīng)存在該名稱的數(shù)據(jù)表
objectStore = db.createObjectStore('table', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('age', 'age', { unique: true });
}
}
db.createObjectStore('table', { keyPath: 'id' })表示新建名稱為table的表,主鍵為id杂拨,主鍵(key)是默認(rèn)建立索引的屬性专普。后面會(huì)介紹詳細(xì)用法
如果數(shù)據(jù)記錄里面沒有合適作為主鍵的屬性,那么可以讓 IndexedDB 自動(dòng)生成主鍵弹沽。
var objectStore = db.createObjectStore('table',{ autoIncrement: true });
objectStore.createIndex('name', 'name', { unique: false })表示建立索引檀夹,可根據(jù)索引查詢指定條件的數(shù)據(jù),可建立多條索引策橘;三個(gè)參數(shù)分別表示為 索引名稱炸渡,索引所在的屬性,該屬性是否包含相同的值
1.2 新增/更新數(shù)據(jù)
function changeData() {
var request = db.transaction(['table'], 'readwrite') //readwrite表示有讀寫權(quán)限
.objectStore('table')
.add({ id: 1, name: 'leiyin', age: 24}); //新增數(shù)據(jù)
或
.put({ id: 1, name: 'leiyin', age: 24}); //更新數(shù)據(jù)
request.onsuccess = function (event) {
console.log('數(shù)據(jù)寫入成功');
};
request.onerror = function (event) {
console.log('數(shù)據(jù)寫入失敗');
}
}
changeData();
PS:indexedDB都是異步操作丽已,具體操作可在回調(diào)函數(shù)中寫
1.3 讀取數(shù)據(jù)
(1)根據(jù)主鍵讀取數(shù)據(jù)
objectStore.get()方法用于讀取數(shù)據(jù)蚌堵,參數(shù)是主鍵的值。
function read() {
var transaction = db.transaction(['table']);
var objectStore = transaction.objectStore('table');
var request = objectStore.get(1);
request.onerror = function(event) {
console.log('事務(wù)失敗');
};
request.onsuccess = function( event) {
if (request.result) {
console.log(request.result);
} else {
console.log('未獲得數(shù)據(jù)記錄');
}
};
}
read();
(2) 通過索引讀取數(shù)據(jù)
使用索引能自定義字段進(jìn)行搜索促脉,如果不建立索引則只能通過主鍵搜索辰斋。
var transaction = db.transaction(['table'], 'readonly');
var store = transaction.objectStore('table');
var index = store.index('name');
var request = index.get('leiyin');
request.onsuccess = function (e) {
var result = e.target.result;
if (result) {
// ...
} else {
// ...
}
}
(3) 通過游標(biāo)和索引讀取數(shù)據(jù)
上述只能讀取到匹配條件的第一條數(shù)據(jù)記錄策州,假如要獲取多條滿足條件的數(shù)據(jù)記錄,則要使用游標(biāo)宫仗。
游標(biāo)與索引結(jié)合能將通過游標(biāo)將所有滿足所有條件的數(shù)據(jù)全部拿到够挂。
var store = db.transaction('table','readwrite').objectStore('table');
var index = store.index('name');
var request=index.openCursor(IDBKeyRange.only('leiyin'))
request.onerror = function(e){
}
request.onsuccess = function(e){
console.log('游標(biāo)開始查詢')
var cursor = e.target.result;
if(cursor){//必須要檢查
console.log(cursor);
cursor.continue();//遍歷了存儲(chǔ)對(duì)象中的所有內(nèi)容
}else{
//...
}
};
1.4 刪除數(shù)據(jù)
(1)根據(jù)主鍵刪除數(shù)據(jù)
function remove() {
var request = db.transaction(['table'], 'readwrite')
.objectStore('table')
.delete(1);
request.onsuccess = function (event) {
console.log('數(shù)據(jù)刪除成功');
};
}
remove();
(2) 通過游標(biāo)和索引刪除數(shù)據(jù)
function cursorDeldteData(db,table){
//通過游標(biāo)刪除記錄
var store = db.transaction(table,'readwrite').objectStore(table);
var request = store.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result,
value,
deleteRequest;
if(cursor){
deleteRequest = cursor.delete();//請(qǐng)求刪除當(dāng)前項(xiàng)
deleteRequest.onerror = function(){
console.log('游標(biāo)刪除該記錄失敗');
};
cursor.continue();
}
};
}
1.5 清除數(shù)據(jù)表中的數(shù)據(jù)
由于刪除數(shù)據(jù)庫(kù)后不能重新創(chuàng)建相同名稱的數(shù)據(jù)庫(kù),而項(xiàng)目又需要對(duì)相同名稱的數(shù)據(jù)庫(kù)進(jìn)行操作藕夫,我選擇了清除數(shù)據(jù)庫(kù)中的所有表內(nèi)的數(shù)據(jù)孽糖。
function clear(db,table){
//刪除存儲(chǔ)空間全部記錄
var store = db.transaction(table,'readwrite').objectStore(table);
store.clear();
console.log('已刪除存儲(chǔ)空間'+table+'全部記錄');
}
clear();
1.6 刪除數(shù)據(jù)庫(kù)
indexedDB.deleteDatabase(DatabaseName);
經(jīng)測(cè)試發(fā)現(xiàn),刪除數(shù)據(jù)庫(kù)成功后毅贮,不能再創(chuàng)建相同名稱的數(shù)據(jù)庫(kù)办悟。
2.碰到的問題
2.1 IOS兼容性問題
問題描述:
兼容性問題,indexedDB中使用索引與游標(biāo)結(jié)合可刪除滿足條件的多條數(shù)據(jù)滩褥,在Android中可刪除多條病蛉,但在iOS中只能刪除一條滿足條件的數(shù)據(jù)
問題分析
經(jīng)反復(fù)測(cè)試,發(fā)現(xiàn)iOS中cursor.continue()方法失效瑰煎,該方法作用是繼續(xù)向下匹配铺然。
問題解決
1.使用遞歸方法實(shí)現(xiàn)功能。
2.但是測(cè)試后發(fā)現(xiàn)cursor.continue()方法刪除數(shù)據(jù)要更快一些酒甸。
3為了提高性能魄健,.判斷當(dāng)前設(shè)備是iOS還是Android,假如為iOS使用遞歸插勤,為Android使用cursor.continue()沽瘦。