新聞中心
在html 5中,其中一個(gè)引人注意的新特性,那就是允許使用Indexed DB。用戶可以從這個(gè)鏈接(http://www.w3.org/TR/IndexedDB/)了解到Indexed DB的詳細(xì)標(biāo)準(zhǔn)。在本文中,將對Indexed DB作簡單的入門介紹。

專注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)墨玉免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了近千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
概述
從本質(zhì)上說,IndexedDB允許用戶在瀏覽器中保存大量的數(shù)據(jù)。任何需要發(fā)送大量數(shù)據(jù)的應(yīng)用都可以得益于這個(gè)特性,可以把數(shù)據(jù)存儲在用戶的瀏覽器端。當(dāng)前這只是IndexedDB的其中一項(xiàng)功能,IndexedDB也提供了強(qiáng)大的基于索引的搜索api功能以獲得用戶所需要的數(shù)據(jù)。
用戶可能會問:IndexedDB是和其他以前的存儲機(jī)制(如cookie,session)有什么不同?
Cookies是最常用的瀏覽器端保存數(shù)據(jù)的機(jī)制,但其保存數(shù)據(jù)的大小有限制并且有隱私問題。Cookies并且會在每個(gè)請求中來回發(fā)送數(shù)據(jù),完全沒辦法發(fā)揮客戶端數(shù)據(jù)存儲的優(yōu)勢。
再來看下Local Storage本地存儲機(jī)制的特點(diǎn)。Local Storage在HTML 5中有不錯(cuò)的支持,但就總的存儲量而言依然是有所限制的。Local Storage并不提供真正的“檢索API”,本地存儲的數(shù)據(jù)只是通過鍵值對去訪問。Local Storage對于一些特定的需要存儲數(shù)據(jù)的場景是很適合的,例如,用戶的喜好習(xí)慣,而IndexedDB則更適合存儲如廣告等數(shù)據(jù)(它更象一個(gè)真正的數(shù)據(jù)庫)。
在我們進(jìn)一步探討Indexed DB前,我們先看下目前的主流瀏覽器對Indexed DB的支持。 IndexedDB目前依然是一個(gè)w3c中候選的建議規(guī)范,在這一點(diǎn)上規(guī)范目前還是令人感到滿意的,但現(xiàn)在正在尋找開發(fā)者社區(qū)的反饋。該規(guī)范可能會在到***確認(rèn)階段前會因應(yīng)w3c的建議有所變化。在一般情況下,目前的瀏覽器對IndexedDB的支持都以比較統(tǒng)一的方式實(shí)現(xiàn),但開發(fā)者應(yīng)注意在未來的更新及對此作出一定的修改。
我們來看來自CanIUse.com的對于各瀏覽器對IndexedDB的支持的圖表,可以看到,目前桌面端瀏覽器對其的支持是不錯(cuò)的,但移動(dòng)端瀏覽器的支持就比較少了:
Chrome for Android支持IndexedDB,但很少人目前在Android設(shè)備上使用這款瀏覽器。是否缺乏移動(dòng)端瀏覽器的支持就意味著不應(yīng)該使用它呢?當(dāng)然不是!幸好我們的開發(fā)者都懂得持續(xù)改進(jìn)的概念。象IndexedDB這樣的特性可以用其他的方式添加到那些不支持該功能的瀏覽器中。用戶可以使用包裝過的類庫去轉(zhuǎn)換到移動(dòng)端的WebSQL,又或者干脆不在移動(dòng)端進(jìn)行本地存儲數(shù)據(jù)。我個(gè)人認(rèn)為能在客戶端緩存大量的數(shù)據(jù),對使用上來說是很重要的功能,即使缺乏移動(dòng)端的支持。
開始學(xué)習(xí)
首先,在使用IndexedDB前,要做的是檢查當(dāng)前的瀏覽器對IndexedDB是否支持,做法只需要使用如下代碼就可以實(shí)現(xiàn):
- document.addEventListener("DOMContentLoaded", function(){
- if("indexedDB" in window) {
- console.log("YES!!! I CAN DO IT!!! WOOT!!!");
- } else {
- console.log("I has a sad.");
- }
- },false);
上面的代碼中(可以在本文下載代碼中的test1.html中找到),使用了DOMContentLoaded事件在加載的過程中,通過判斷在window對象中是否存在indexedDB,當(dāng)然為了在接下來的過程中記住判斷的最終結(jié)果,可以使用如下的代碼更好地保存(test2.html):
- var idbSupported = false;
- document.addEventListener("DOMContentLoaded", function(){
- if("indexedDB" in window) {
- idbSupported = true;
- }
- },false);
操作數(shù)據(jù)庫
下面要講解的是如何操作IndexedDB數(shù)據(jù)庫。首先要了解的是,IndexedDB并不象傳統(tǒng)的如SQL Server那樣需要額外安裝。Indexed是存在于瀏覽器端的并且能被用戶所訪問控制。IndexedDB和cookies和local storage的原則是一樣的,就是一個(gè)IndexedDB是和一個(gè)唯一的DOMAIN相關(guān)聯(lián)的。比如名為“Foo”的數(shù)據(jù)庫是由foo.com所關(guān)聯(lián)的,是不會和goo.com所創(chuàng)建的同名“Foo”數(shù)據(jù)庫沖突的,因?yàn)樗麄儗儆诓煌膁omain,并且他們之間是不能互相訪問的。
打開一個(gè)數(shù)據(jù)庫是通過命令執(zhí)行的?;镜挠梅ㄊ翘峁?shù)據(jù)庫的名稱和版本號即可,其中版本是十分重要的,稍候會作解析。下面是基本的例子:
- var openRequest = indexedDB.open("test",1);
打開一個(gè)IndexedDB數(shù)據(jù)庫是異步的操作。為了處理操作的返回結(jié)果,需要增加一些事件的監(jiān)聽,目前有四種不同類型的事件監(jiān)聽事件:
- success
- error
- upgradeneeded
- blocked
大家可能已經(jīng)能知道success和error事件的含義了。而upgradeneeded事件是在***打開數(shù)據(jù)庫或者改變數(shù)據(jù)庫版本的時(shí)候被觸發(fā)。blocked事件是在前一個(gè)連接沒有被關(guān)閉的時(shí)候被觸發(fā)。
讓我們看下接下來的例子(test3.html),其中當(dāng)***訪問網(wǎng)站的時(shí)候會觸發(fā)upgradeneeded事件,然后是success事件。
- var idbSupported = false;
- var db;
- document.addEventListener("DOMContentLoaded", function(){
- if("indexedDB" in window) {
- idbSupported = true;
- }
- if(idbSupported) {
- var openRequest = indexedDB.open("test",1);
- 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);
- }
- }
- },false);
#p#
在上面的代碼中,我們再一次檢查當(dāng)前瀏覽器是否支持IndexedDB,如果支持,則打開一個(gè)數(shù)據(jù)庫。在這段代碼中我們使用了三個(gè)事件 ――upgrade事件、成功事件和錯(cuò)誤事件。首先看success事件,該事件通過target.result傳入句柄,然后將其復(fù)制到一個(gè)全局變量db中,用作稍候添加數(shù)據(jù)用。如果用戶在支持IndexedDB的瀏覽器中運(yùn)行上面的代碼,應(yīng)該會在控制臺看到看到Upgrading...和success的輸出信息,如果再次運(yùn)行的話,則只會看到輸出成功的信息,因?yàn)閡pgradedneed事件只在***打開數(shù)據(jù)庫或升級的時(shí)候被調(diào)用。
對象存儲
接下來,我們學(xué)習(xí)如何存儲數(shù)據(jù)。IndexedDB有一個(gè)概念稱為“對象存儲”。
用戶可以認(rèn)為這是一張典型的關(guān)系數(shù)據(jù)庫中的表。對象中當(dāng)然會存儲了數(shù)據(jù),但也有一個(gè)keypath和可選的索引集合。所謂的Keypaths是用戶數(shù)據(jù)的唯一標(biāo)識,并且可以用不同的格式表示。索引則在后面會進(jìn)行講解。
還記得之前提到的upgrademeeded事件么?要注意的是只能在upgradeneeded事件中創(chuàng)建一個(gè)對象。目前,默認(rèn)是在用戶***訪問你的網(wǎng)站的時(shí)候會自動(dòng)創(chuàng)建對象。同時(shí)如果要修改你的對象,則必須更新數(shù)據(jù)的版本,并且要為此編寫代碼,讓我們來看代碼的例子如下:
- var idbSupported = false;
- var db;
- document.addEventListener("DOMContentLoaded", function(){
- if("indexedDB" in window) {
- idbSupported = true;
- }
- if(idbSupported) {
- var openRequest = indexedDB.open("test_v2",1);
- openRequest.onupgradeneeded = function(e) {
- console.log("running onupgradeneeded");
- var thisDB = e.target.result;
- if(!thisDB.objectStoreNames.contains("firstOS")) {
- thisDB.createObjectStore("firstOS");
- }
- }
- openRequest.onsuccess = function(e) {
- console.log("Success!");
- db = e.target.result;
- }
- openRequest.onerror = function(e) {
- console.log("Error");
- console.dir(e);
- }
- }
- },false);
上面的代碼(見test4.html)中,請看upgradeneeded事件,首先是通過變量thisDB獲得了打開的數(shù)據(jù)庫,這個(gè)變量的其中一個(gè)屬性是一個(gè)已存在的對象存儲的list,名為objectStoreNames。我們可以使用contains方法檢查某個(gè)對象是否已經(jīng)存在了,如果不存在則可以進(jìn)行創(chuàng)建,使用的方法是createObjectStore,因?yàn)檫@個(gè)是IndexedDB的同步操作,因此不需要為此進(jìn)行事件監(jiān)聽。
總結(jié)一下,當(dāng)用戶訪問你的網(wǎng)站時(shí),如果用戶的瀏覽器支持IndexedDB,則首先觸發(fā)的是upgradeneeded事件,代碼中檢查是否有“firstOS”對象存在,如果沒有的話則新創(chuàng)建一個(gè),當(dāng)用戶第二次訪問網(wǎng)站的時(shí)候,數(shù)據(jù)庫的版本依然和***次訪問時(shí)是相同的。
假如需要新增加另外一個(gè)對象存儲,則只需要增加版本號并復(fù)制上面contains/ createObjectStore部分的代碼,代碼如下(見test5.html):
- var openRequest = indexedDB.open("test_v2",2);
- openRequest.onupgradeneeded = function(e) {
- console.log("running onupgradeneeded");
- var thisDB = e.target.result;
- if(!thisDB.objectStoreNames.contains("firstOS")) {
- thisDB.createObjectStore("firstOS");
- }
- if(!thisDB.objectStoreNames.contains("secondOS")) {
- thisDB.createObjectStore("secondOS");
- }
- }
如何增加數(shù)據(jù)
下面我們可以開始增加數(shù)據(jù)。和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫有點(diǎn)不同,IndexedDB允許保存對象。這意味著開發(fā)者甚至可以保存一個(gè)Javascript對象。對于數(shù)據(jù)查詢是需要使用事務(wù),事務(wù)需要兩個(gè)參數(shù),***個(gè)是將要處理的表的數(shù)組,其中的元素是表,第二個(gè)參數(shù)是事務(wù)的類型。目前有兩種事務(wù)的類型:只讀和讀寫。增加數(shù)據(jù)是屬于讀寫操作,代碼如下:
- var transaction = db.transaction(["people"],"readwrite");
這里指出了要設(shè)置存儲對象people為讀寫操作,然后使用objectStore指定要操作的存儲對象,存儲到變量store中去
- var store = transaction.objectStore("people");
接下來就可以增加數(shù)據(jù)了,我們定義一個(gè)person對象,然后增加到這個(gè)store中去:
- var person = {
- name:name,
- email:email,
- created:new Date()
- }
- var request = store.add(person,1);
可以看到,這里聲明了一個(gè)Javascript的普通對象,然后使用store的add方法就可以增加這個(gè)對象到對象存儲中,其中add的第2個(gè)參數(shù)是標(biāo)識數(shù)據(jù)的唯一標(biāo)識,在這里只是暫時(shí)硬編碼了,在接下來我們將不再使用硬編碼的方法。
要注意增加數(shù)據(jù)的操作是異步的,所以增加兩個(gè)事件監(jiān)聽,代碼如下:
- request.onerror = function(e) {
- console.log("Error",e.target.error.name);
- }
- request.onsuccess = function(e) {
- console.log("Woot! Did it");
- }
現(xiàn)在我們看下test6.html,這個(gè)是包含html的完整代碼:
#p#
在瀏覽器中運(yùn)行這段代碼,則可以看到如下圖的HTML頁面,其中在文本框輸入內(nèi)容,點(diǎn)按鈕,則會在瀏覽器的調(diào)試工具中(我們使用的是Chrome)看到有如下的輸出:
.如果使用Chrome,則可以充分利用其調(diào)試工具中對IndexedDB的可視化支持,如果點(diǎn)資源的TAB,展開IndexedDB的部分,則可以看到如下圖所示的,我們之前創(chuàng)建的people存儲對象。
接下來我們嘗試再次點(diǎn)擊增加按鈕,這個(gè)時(shí)候注意到會輸出錯(cuò)誤的提示信息如下圖,其信息表示試圖增加一個(gè)已經(jīng)存儲的對象,違反數(shù)據(jù)約束了:
關(guān)于Key
Keys是IndexedDB的主鍵,傳統(tǒng)的關(guān)系數(shù)據(jù)庫可以沒有keys,但每個(gè)對象存儲都必須有一個(gè)key。IndexedDB允許多個(gè)不同類型的KEY。
***種方法是象上文中的那樣手工指定。第二種方法是使用keypath,其中的key是基于數(shù)據(jù)自身的屬性,比如people中的例子可以使用email地址作為key。第三種方法是建議采用的方法,就是使用key生成器,這有點(diǎn)象自動(dòng)編號的主鍵的方法。下面是關(guān)于key的兩個(gè)例子,其中一個(gè)是使用keypath,另外的是使用key生成器:
- hisDb.createObjectStore("test", { keyPath: "email" });
- thisDb.createObjectStore("test2", { autoIncrement: true });
并且我們可以修改上一個(gè)例子中,用帶key的方法創(chuàng)建對象:
- thisDB.createObjectStore("people", {autoIncrement:true});
這樣的話,就可以取消之前硬編碼的做法了:
- var request = store.add(person);
讀取數(shù)據(jù)
再來看如何讀取數(shù)據(jù),讀取數(shù)據(jù)要在一個(gè)事務(wù)中進(jìn)行并且是異步的,下面是例子:
- var transaction = db.transaction(["test"], "readonly");
- var objectStore = transaction.objectStore("test");
- var ob = objectStore.get(x);
- ob.onsuccess = function(e) {
- }
在上面的代碼中,首先設(shè)置為只讀的事務(wù)。然后使用objectStore.get方法就可以了,要注意編寫其onsuccess事件,也可以使用鏈?zhǔn)秸{(diào)用,如下:
- db.transaction(["test"], "readonly").objectStore("test").get(X).onsuccess = function(e) {}
現(xiàn)在我們在test8.html中,為我們之前的例子加上讀取數(shù)據(jù)的部分,運(yùn)行后的效果如下圖:
其中讀取數(shù)據(jù)的代碼為:
- function getPerson(e) {
- var key = document.querySelector("#key").value;
- if(key === "" || isNaN(key)) return;
- var transaction = db.transaction(["people"],"readonly");
- var store = transaction.objectStore("people");
- var request = store.get(Number(key));
- request.onsuccess = function(e) {
- var result = e.target.result;
- console.dir(result);
- if(result) {
- var s = "
Key "+key+"
";
- for(var field in result) {
- s+= field+"="+result[field]+"
";- }
- document.querySelector("#status").innerHTML = s;
- } else {
- document.querySelector("#status").innerHTML = "
No match
";- }
- }
- }
#p#
如何讀取更多數(shù)據(jù)
如何讀取批量的數(shù)據(jù)?也就是象傳統(tǒng)關(guān)系數(shù)據(jù)庫中讀取數(shù)據(jù)集?IndexedDB支持游標(biāo)的概念,這對有數(shù)據(jù)庫基礎(chǔ)的讀者來說是很容易理解的,下面的代碼演示了如何讀取數(shù)據(jù)集:
- var transaction = db.transaction(["test"], "readonly");
- var objectStore = transaction.objectStore("test");
- var cursor = objectStore.openCursor();
- cursor.onsuccess = function(e) {
- var res = e.target.result;
- if(res) {
- console.log("Key", res.key);
- console.dir("Data", res.value);
- res.continue();
- }
- }
其中使用objectStore的openCursor打開游標(biāo),并且在onsuccess事件中進(jìn)行處理,注意使用res獲得了結(jié)果集,然后用res.continue()方法去循環(huán)讀取記錄集。同樣,我們用這個(gè)方法去遍歷之前的people存儲集,代碼如下:
- function getPeople(e) {
- var s = "";
- db.transaction(["people"], "readonly").objectStore("people").openCursor().onsuccess = function(e) {
- var cursor = e.target.result;
- if(cursor) {
- s += "
Key "+cursor.key+"
";
- for(var field in cursor.value) {
- s+= field+"="+cursor.value[field]+"
";- }
- s+="";
- cursor.continue();
- }
- document.querySelector("#status2").innerHTML = s;
- }
- }
可以在test9.html中看到完整的代碼,我們可以不斷增加人員信息,然后點(diǎn)獲取所有信息,如下圖運(yùn)行效果:
IndexedDB中的索引
在本教程的***部分,講解IndexedDB中最重要的部分就是索引了。首先是如何創(chuàng)建索引,這需要在upgrade事件中進(jìn)行,下面是方法:
- var objectStore = thisDb.createObjectStore("people",
- { autoIncrement:true });
- objectStore.createIndex("name","name", {unique:false});
- objectStore.createIndex("email","email", {unique:true});
其中,在objectStore.createIndex("name","name", {unique:false});
中,***個(gè)參數(shù)就是索引的名稱,第二個(gè)參數(shù)就是列。并且使用unique指定某個(gè)列是否唯一,這里只是認(rèn)為email是唯一的,name是不唯一的。
那么如何使用索引呢?一旦我們在事務(wù)中獲得對象存儲,則可以通過索引去獲得數(shù)據(jù),一個(gè)例子如下:
- var transaction = db.transaction(["people"],"readonly");
- var store = transaction.objectStore("people");
- var index = store.index("name");
- var request = index.get(name);
接下來,可以在onsuccess事件中進(jìn)行編碼處理,參考test10.html,代碼如下:
- request.onsuccess = function(e) {
- var result = e.target.result;
- if(result) {
- var s = "
Name "+name+"
";
- for(var field in result) {
- s+= field+"="+result[field]+"
";- }
- document.querySelector("#status").innerHTML = s;
- } else {
- document.querySelector("#status").innerHTML = "
No match
";- }
- }
接下來看下另外一個(gè)基于索引的Range的用法,通過Range,可以獲得某個(gè)數(shù)據(jù)范圍之間的數(shù)據(jù),比如字母a到字母c之間的所有name,同時(shí)還可以設(shè)置是否包含或者不包含邊界(如是否包含字母a),并且可以對范圍之間的數(shù)據(jù)進(jìn)行排序,來看例子:
- //大于39
- var oldRange = IDBKeyRange.lowerBound(39);
- //大于40
- var oldRange2 = IDBKeyRange.lowerBound(40,true);
- //小于39
- var youngRange = IDBKeyRange.upperBound(40);
- //小于39
- var youngRange2 = IDBKeyRange.upperBound(39,true);
- //20到40之間
- var okRange = IDBKeyRange.bound(20,40)
這里使用的是一個(gè)頂層的對象名為IDBKeyRange。其中l(wèi)owerBound方法指定返回的數(shù)據(jù)是大于指定的參數(shù)的,upperBound則相反,bound則返回指定范圍之間的數(shù)據(jù)。下面給出一個(gè)檢索的例子,其中在頁面表單中可以輸入搜索名稱的開始和結(jié)束范圍,如下:
- Starting with:
- Ending with:
其事件代碼為:
- function getPeople(e) {
- var name = document.querySelector("#nameSearch").value;
- var endname = document.querySelector("#nameSearchEnd").value;
- if(name == "" && endname == "") return;
- var transaction = db.transaction(["people"],"readonly");
- var store = transaction.objectStore("people");
- var index = store.index("name");
- if(name != "" && endname != "") {
- range = IDBKeyRange.bound(name, endname);
- } else if(name == "") {
- range = IDBKeyRange.upperBound(endname);
- } else {
- range = IDBKeyRange.lowerBound(name);
- }
- var s = "";
- index.openCursor(range).onsuccess = function(e) {
- var cursor = e.target.result;
- if(cursor) {
- s += "
Key "+cursor.key+"
";
- for(var field in cursor.value) {
- s+= field+"="+cursor.value[field]+"
";- }
- s+="";
- cursor.continue();
- }
- document.querySelector("#status").innerHTML = s;
- }
- }
在上面的代碼中,我們首先判斷用戶到底在哪個(gè)文本框中輸入了檢索條件,然后通過條件判斷組合成了range,然后將range傳遞給打開游標(biāo)的方法index.openCursor(range)就可以了,IndexedDB則會自動(dòng)檢索出符合條件的記錄,例子的代碼在test11.html。
在接下來的第二部分的教程中,將探討包括更新、刪除和數(shù)組方面的操作,敬請期待。
本文代碼下載參見:https://github.com/tutsplus/working-with-indexeddb
新聞名稱:IndexedDB入門導(dǎo)學(xué)
標(biāo)題網(wǎng)址:http://www.5511xx.com/article/cosgdhd.html


咨詢
建站咨詢
