客戶端持久化解決方案:indexedDB

客戶端持久化解決方案:indexedDB

indexedDB適合大量的結構化的數據存儲;打開數據庫和獲取數據對象都是異步的;
須要開啓事務,訪問的objectStore都要是在開啓的事務中。
數據庫結構: db->transaction->objectStore->datamysql

Web SQL Database實際上已經被廢棄,而HTML5支持的本地存儲實際上變成了 Web StorageLocal Storage和Session Storage)與 IndexedDBsql

Web Storage使用簡單字符串鍵值對在本地存儲數據,方便靈活,可是對於大量結構化數據存儲力不從心,IndexedDB是爲了可以在客戶端存儲大量的結構化數據,而且使用索引高效檢索的API。數據庫

indexedDB最顯著的特色: 異步API

在IndexedDB大部分操做(如:打開數據庫和獲取數據)並非同步的,如:api

var request=window.indexedDB.open('testDB');

這條指令並不會返回一個DB對象的句柄,咱們獲得的是一個IDBOpenDBRequest對象,而咱們但願獲得的DB對象在IDBOpenDBRequest.result屬性中.數據結構


indexedDB的經常使用操做

建立/打開數據庫

function openDB (name) {
    var idbRequest=window.indexedDB.open(name);
    idbRequest.onerror=function(e){
        console.log('OPen Error!');
    };
    idbRequest.onsuccess=function(e){
        var db=e.target.result;
        console.log('db: %o', db);
    };
}
openDB(dbName);

除了onerror和onsuccess,IDBOpenDBRequest還有一個相似回調函數句柄 onupgradeneeded。這個句柄在咱們請求打開的數據庫的版本號和已經存在的數據庫版本號不一致的時候調用。併發

// indexedDB.open(dbName, version);
function openDB (name, version) {
    version = version || 1;
    //打開或建立數據庫
    var idbRequest = window.indexedDB.open(name, version);
    idbRequest.onerror = function(e){
        console.warn('error: %s', e.currentTarget.error.message);
    };
    
    idbRequest.onsuccess = function(e){
        db = e.target.result; //這裏纔是 indexedDB對象
        console.log('idbRequest === e.target: %o', idbRequest === e.target);
        console.log('db: %o, idbRequest: %o', db, idbRequest);
    };

    // 注意: 只能請求>=當前數據庫版本號的版本
    //  大於當前版本號, 則觸發 onupgradeneeded, 
    //  小於當前版本號,則觸發 onerror
    idbRequest.onupgradeneeded = function(e){
        console.log('DB version change to ' + version);
        var db = e.target.result;
        console.log('onupgradeneeded: db->', db);
    };
}

刪除數據庫

window.indexedDB.deleteDatabase(dbName);

關閉數據庫

db.onclose = function(){
    //do sth..
};
db.close();

建立 objectStore

有了數據庫後咱們天然但願建立一個表用來存儲數據,但indexedDB中沒有表的概念,而是叫 objectStore ,一個數據庫中能夠包含多個objectStoreobjectStore是一個靈活的數據結構,能夠存放多種類型數據。也就是說一個objectStore至關於一張表,裏面存儲的每條數據和一個鍵相關聯。異步

咱們可使用每條記錄中的某個指定字段做爲鍵值(keyPath 如: {keyPath: 'id'} ),也可使用自動生成的遞增數字做爲鍵值(keyGenerator 如: {autoincrement: true} kk:很像mysql autoincrement字段),也能夠不指定。函數

鍵類型 存儲數據
不使用 任意值,可是每添加一條數據的時候,需指定鍵參數
keyPath 對象,eg: {keyPath: 'id'}
keyGenerator 任意值 eg: {autoincrement: true}
keyPath and KeyGenerator 都使用 對象,若是對象中有keyPath指定的屬性則不生成新的鍵值,若是沒有自動生成遞增鍵值,填充keyPath指定的屬性

事務fetch

在對新數據庫作任何事情以前,須要開始一個事務。事務中須要指定該事務跨越哪些 objectStore.code

事務具備三種模式:

  • 只讀:read,不能修改數據庫數據,能夠併發執行
  • 讀寫:readwrite,能夠進行讀寫操做
  • 版本變動:verionchange

    //打開一個事務,使用students 和teacher objectStore
    var transaction=db.transaction([students','taecher']);
    //獲取students objectStore
    var objectStore=transaction.objectStore('students');

    //建立objectStore
    db.createObjectStore(storeName, keyPath);

由於對新數據的操做都須要在transaction中進行,而transaction又要求指定objectStore,因此咱們只能在建立數據庫的時候初始化objectStore以供後面使用,這正是onupgradeneeded的一個重要做用。

function openDB (name,version) {
    var version=version || 1;
    var idbRequest=window.indexedDB.open(name,version);
    idbRequest.onerror=function(e){
        console.log(e.currentTarget.error.message);
    };
    idbRequest.onsuccess=function(e){
        myDB.db=e.target.result;
    };
    idbRequest.onupgradeneeded=function(e){
        var db=e.target.result;
        if(!db.objectStoreNames.contains('students')){
            //db.createObjectStore('students',{autoIncrement: true});//keyGenerator
            db.createObjectStore('students',{keyPath:"id"});
        }
        console.log('DB version changed to '+version);
    };
}

刪除objectStore

db.deleteObjectStore(storeName); //注意:需在onupgradeneeded鉤子中執行

保存數據到objectStore

function saveData (dbName, version, storeName, data) {
    var idbRequest = indexedDB.open(dbName, version);

    idbRequest.onsuccess = function (e) {
        var db = idbRequest.result; 
        var transaction = db.transaction(storeName, 'readwrite');//需先建立事務
        var store = transaction.objectStore(storeName); //訪問事務中的objectStore
        data.forEach(function (item) {
            store.add(item);//保存數據
        });
        console.log('save data done..');
    }
}

save data

查找數據

function getDataByKey(db,storeName,key){
    var transaction=db.transaction(storeName,'readwrite'); 
    var store=transaction.objectStore(storeName); 
    var dataRequest=store.get(key); 
    dataRequest.onsuccess=function(e){//異步的
        var student=e.target.result; 
        console.log(student.name); 
    };
}

更新數據

能夠調用objectStore的put方法更新數據,會自動替換鍵值相同的記錄,達到更新目的,沒有相同的則添加。

function updateDataByKey(db,storeName,student){
    var transaction=db.transaction(storeName,'readwrite'); 
    var store=transaction.objectStore(storeName); 
    store.put(student); 
}

刪除數據

function deleteDataByKey(db,storeName,key){
    var transaction=db.transaction(storeName,'readwrite'); 
    var store=transaction.objectStore(storeName); 
    store.delete(key); 
}

清空數據

function clearObjectStore(db,storeName){
    var transaction=db.transaction(storeName,'readwrite'); 
    var store=transaction.objectStore(storeName); 
    store.clear();
}

indexedDB的索引

熟悉數據庫的同窗都知道索引的一個好處就是能夠迅速定位數據,提升搜索速度,在indexedDB中有兩種索引,一種是自增加的int值,一種是keyPath:本身指定索引列,咱們重點來看看keyPath方式的索引使用.

建立索引

咱們能夠在db.createObjectStore(storeName, keyPath)時用 store.createIndex(indexName, key, opts)來指明索引。

function openDB (name,version) {
    var version=version || 1;
    var request=window.indexedDB.open(name,version);
    request.onerror=function(e){
        console.log(e.currentTarget.error.message);
    };
    request.onsuccess=function(e){
        myDB.db=e.target.result;
    };
    request.onupgradeneeded=function(e){
        var db=e.target.result;
        if(!db.objectStoreNames.contains('students')){
            var store=db.createObjectStore('students',{keyPath: 'id'});
            //在students 上建立了兩個索引
            store.createIndex('nameIndex','name',{unique:true}); 
            store.createIndex('ageIndex','age',{unique:false}); 
        }
        console.log('DB version changed to '+version);
    };
}

createIndex

利用索引快速獲取數據,name的索引是惟一的沒問題,可是對於age索引只會取到第一個匹配值,要想獲得全部age符合條件的值就須要使用遊標了

function getDataByIndex(db,storeName){
    var transaction=db.transaction(storeName);
    var store=transaction.objectStore(storeName);
    var index = store.index("nameIndex");//獲取索引
    index.get('Byron').onsuccess=function(e){//經過索引獲取數據
        var student=e.target.result;
        console.log(student.id);
    }
}

遊標

在indexedDB中使用索引和遊標是分不開的,對數據庫熟悉的同窗很好理解遊標是什麼東東,有了數據庫objectStore的遊標,咱們就能夠利用遊標遍歷objectStore了。

objectStore.openCursor(); //打開遊標

function fetchStoreByCursor(db,storeName){
    var transaction=db.transaction(storeName);
    var store=transaction.objectStore(storeName);
    var request=store.openCursor();
    request.onsuccess=function(e){
        var cursor=e.target.result;
        if(cursor){
            console.log(cursor.key);
            var currentStudent=cursor.value;
            console.log(currentStudent.name);
            cursor.continue();
        }
    };
}

index與遊標結合

要想獲取age爲26的student,能夠結合遊標使用索引

function getMultipleData(db,storeName){
    var transaction=db.transaction(storeName);
    var store=transaction.objectStore(storeName);
    var index = store.index("ageIndex");
    var request=index.openCursor(IDBKeyRange.only(26))
    request.onsuccess=function(e){
        var cursor=e.target.result;
        if(cursor){
            var student=cursor.value;
            console.log(student.id);
            cursor.continue();
        }
    }
}

這樣咱們但是使用索引打開一個遊標,在成功的句柄內得到遊標遍歷age爲26的student,也能夠經過index.openKeyCursor()方法只獲取每一個對象的key值。

指定遊標範圍

index.openCursor()/index.openKeyCursor() 方法在不傳遞參數的時候會獲取objectStore全部記錄,像上面例子同樣咱們能夠對搜索進行篩選
可使用 IDBKeyRange 限制遊標中值的範圍,把它做爲第一個參數傳給 openCursor() 或是 openKeyCursor()

  • IDBKeyRange.only(value):只獲取指定數據
  • IDBKeyRange.lowerBound(value,isOpen):獲取最小是value的數據,第二個參數用來指示是否排除value值自己,也就是數學中的是不是開區間
  • IDBKeyRange.upperBound(value,isOpen):和上面相似,用於獲取最大值是value的數據
  • IDBKeyRange.bound(value1,value2,isOpen1,isOpen2):不用解釋了吧

    // 獲取名字首字母在B-E的student
    function getMultipleData(db,storeName){
    var transaction=db.transaction(storeName);
    var store=transaction.objectStore(storeName);
    var index = store.index("nameIndex");
    var request=index.openCursor(IDBKeyRange.bound('B','F',false, true ));
    request.onsuccess=function(e){
    var cursor=e.target.result;
    if(cursor){
    var student=cursor.value;
    console.log(student.name);
    cursor.continue();
    }
    }
    }

有了遊標和索引才能真正發揮indexedDB威力

相關文章
相關標籤/搜索