HTML5本地存儲——IndexedDB

HTML5本地存儲——Web SQL Database提到過Web SQL Database實際上已經被廢棄,而HTML5的支持的本地存儲實際上變成了html

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

異步API

在IndexedDB大部分操做並非咱們經常使用的調用方法,返回結果的模式,而是請求——響應的模式,好比打開數據庫的操做chrome

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

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

image

這條指令請求的響應是一個 IDBDatabase對象,這就是IndexedDB對象,數據結構

image

 

除了result,IDBOpenDBRequest接口定義了幾個重要屬性併發

  • onerror: 請求失敗的回調函數句柄
  • onsuccess:請求成功的回調函數句柄
  • onupgradeneeded:請求數據庫版本變化句柄

 

所謂異步API是指並非這條指令執行完畢,咱們就可使用request.result來獲取indexedDB對象了,就像使用ajax同樣,語句執行完並不表明已經獲取到了對象,因此咱們通常在其回調函數中處理。異步

建立數據庫

剛纔的語句已經展現瞭如何打開一個indexedDB數據庫,調用indexedDB.open方法就能夠建立或者打開一個indexedDB。看一個完整的處理函數

複製代碼
複製代碼
function openDB (name) {
            var request=window.indexedDB.open(name);
            request.onerror=function(e){
                console.log('OPen Error!');
            };
            request.onsuccess=function(e){
                myDB.db=e.target.result;
            };
        }

        var myDB={
            name:'test',
            version:1,
            db:null
        };
        openDB(myDB.name);
複製代碼
複製代碼

代碼中定義了一個myDB對象,在建立indexedDB request的成功毀掉函數中,把request獲取的DB對象賦值給了myDB的db屬性,這樣就可使用myDB.db來訪問建立的indexedDB了。fetch

version

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

indexedDB.open()方法還有第二個可選參數,數據庫版本號,數據庫建立的時候默認版本號爲1,當咱們傳入的版本號和數據庫當前版本號 不一致的時候onupgradeneeded就會被調用,固然咱們不能試圖打開比當前數據庫版本低的version,不然調用的就是onerror了,修 改一下剛纔例子

複製代碼
複製代碼
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){
                console.log('DB version changed to '+version);
            };
        }

        var myDB={
            name:'test',
            version:3,
            db:null
        };
        openDB(myDB.name,myDB.version);
複製代碼
複製代碼

因爲剛纔已經建立了版本爲1的數據庫,打開版本爲3的時候,會在控制檯輸出:DB version changed to 3

關閉與刪除數據庫

關閉數據庫能夠直接調用數據庫對象的close方法

function closeDB(db){
            db.close();
        }

刪除數據庫使用indexedDB對象的deleteDatabase方法

function deleteDB(name){
            indexedDB.deleteDatabase(name);
        }

簡單調用

複製代碼
複製代碼
var myDB={
            name:'test',
            version:3,
            db:null
        };
        openDB(myDB.name,myDB.version);
        setTimeout(function(){
            closeDB(myDB.db);
            deleteDB(myDB.name);
        },500);
複製代碼
複製代碼

因爲異步API願意,不能保證可以在closeDB方法調用前獲取db對象(實際上獲取db對象也比執行一條語句慢得多),因此用了 setTimeout延遲了一下。固然咱們注意到每一個indexedDB實例都有onclose回調函數句柄,用以數據庫關閉的時候處理,有興趣同窗能夠 試試,原理很簡單,不演示了。

 

object store

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

咱們可使用每條記錄中的某個指定字段做爲鍵值(keyPath),也可使用自動生成的遞增數字做爲鍵值(keyGenerator),也能夠不指定。選擇鍵的類型不一樣,objectStore能夠存儲的數據結構也有差別

鍵類型 存儲數據
不使用 任意值,可是沒添加一條數據的時候須要指定鍵參數
keyPath Javascript對象,對象必須有一屬性做爲鍵值
keyGenerator 任意值
都使用 Javascript對象,若是對象中有keyPath指定的屬性則不生成新的鍵值,若是沒有自動生成遞增鍵值,填充keyPath指定屬性

 

事務

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

事務具備三種模式

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

 

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

給object store添加數據

 

調用數據庫實例的createObjectStore方法能夠建立object store,方法有兩個參數:store name和鍵類型。調用store的add方法添加數據。有了上面知識,咱們能夠向object store內添加數據了

 

keyPath

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

複製代碼
複製代碼
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')){
                    db.createObjectStore('students',{keyPath:"id"});
                }
                console.log('DB version changed to '+version);
            };
        }
複製代碼
複製代碼

 

這樣在建立數據庫的時候咱們就爲其添加了一個名爲students的object store,準備一些數據以供添加

複製代碼
複製代碼
var students=[{ 
            id:1001, 
            name:"Byron", 
            age:24 
        },{ 
            id:1002, 
            name:"Frank", 
            age:30 
        },{ 
            id:1003, 
            name:"Aaron", 
            age:26 
        }];
複製代碼
複製代碼
複製代碼
複製代碼
function addData(db,storeName){
            var transaction=db.transaction(storeName,'readwrite'); 
            var store=transaction.objectStore(storeName); 

            for(var i=0;i<students.length;i++){
                store.add(students[i]);
            }
        }


openDB(myDB.name,myDB.version);
        setTimeout(function(){
            addData(myDB.db,'students');
        },1000);
複製代碼
複製代碼

這樣咱們就在students object store裏添加了三條記錄,以id爲鍵,在chrome控制檯看看效果

image

 

keyGenerate

 

複製代碼
複製代碼
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')){
                    db.createObjectStore('students',{autoIncrement: true});
                }
                console.log('DB version changed to '+version);
            };
        }
複製代碼
複製代碼

image

剩下的兩種方式有興趣同窗能夠本身摸索一下了

查找數據

能夠調用object store的get方法經過鍵獲取數據,以使用keyPath作鍵爲例

複製代碼
複製代碼
function getDataByKey(db,storeName,value){
            var transaction=db.transaction(storeName,'readwrite'); 
            var store=transaction.objectStore(storeName); 
            var request=store.get(value); 
            request.onsuccess=function(e){ 
                var student=e.target.result; 
                console.log(student.name); 
            };
}
複製代碼
複製代碼

更新數據

能夠調用object store的put方法更新數據,會自動替換鍵值相同的記錄,達到更新目的,沒有相同的則添加,以使用keyPath作鍵爲例

複製代碼
複製代碼
function updateDataByKey(db,storeName,value){
            var transaction=db.transaction(storeName,'readwrite'); 
            var store=transaction.objectStore(storeName); 
            var request=store.get(value); 
            request.onsuccess=function(e){ 
                var student=e.target.result; 
                student.age=35;
                store.put(student); 
            };
}
複製代碼
複製代碼

刪除數據及object store

 

調用object store的delete方法根據鍵值刪除記錄

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

調用object store的clear方法能夠清空object store

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

調用數據庫實例的deleteObjectStore方法能夠刪除一個object store,這個就得在onupgradeneeded裏面調用了

if(db.objectStoreNames.contains('students')){ 
                    db.deleteObjectStore('students'); 
}

最後

 

這就是關於indexedDB的基本使用方式,不少同窗看了會以爲很雞肋,和咱們正常本身定義個對象使用沒什麼區別,也就是能保存在本地罷了,這是 由於咱們尚未介紹indexedDB之因此稱爲indexed的殺器——索引,這個纔是讓indexedDB大顯神通的東西,下篇咱們就來看看這個殺器。

 

HTML5本地存儲——IndexedDB(一:基本使用)中介紹了關於IndexedDB的基本使用方法,很不過癮,這篇咱們來看看indexedDB的殺器——索引。

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

建立索引

咱們能夠在建立object store的時候指明索引,使用object store的createIndex建立索引,方法有三個參數

  • 索引名稱
  • 索引屬性字段名
  • 索引屬性值是否惟一
複製代碼
複製代碼
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'});
                    store.createIndex('nameIndex','name',{unique:true}); 
                    store.createIndex('ageIndex','age',{unique:false}); 
                }
                console.log('DB version changed to '+version);
            };
        }
複製代碼
複製代碼

這樣咱們在students 上建立了兩個索引

image

 

image

利用索引獲取數據

複製代碼
複製代碼
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);
            }
        }
複製代碼
複製代碼

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

遊標

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

使用object store的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();
                }
            };
        }
複製代碼
複製代碼

curson.contine()會使遊標下移,知道沒有數據返回undefined

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()方法在不傳遞參數的時候會獲取object store全部記錄,像上面例子同樣咱們能夠對搜索進行篩選
可使用key range 限制遊標中值的範圍,把它做爲第一個參數傳給  或是 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();
                }
            }
        }
複製代碼
複製代碼

 完整示例

 

 View Code

 

 

最後

有了遊標和索引才能真正發揮indexedDB爲例,是否是感受比自定義對象強大方便了不少呢。

相關文章
相關標籤/搜索