HTML5的最有趣的功能之一是本地存儲(chǔ)數(shù)據(jù)并且允許應(yīng)用程序離線運(yùn)行的功能。 共有三種不同的處理這些功能的API,如何選中其中之一取決于你希望對(duì)你將要本地存儲(chǔ)的數(shù)據(jù)進(jìn)行怎樣處理:
在用戶的機(jī)器上進(jìn)行本地存儲(chǔ)的最基本的實(shí)現(xiàn)方法是利用web存儲(chǔ)API。 該API使用 key/value 對(duì)來(lái)支持開(kāi)發(fā)人員存儲(chǔ)能夠被 web 應(yīng)用程序訪問(wèn)的基本信息和變量。 該功能的一個(gè)理想用例是用于存儲(chǔ)那些在用戶已經(jīng)瀏覽完并且離開(kāi)應(yīng)用程序或已經(jīng)關(guān)閉 web 瀏覽器之后需要永久保留的簡(jiǎn)單數(shù)據(jù)。 例如,保存游戲狀態(tài)、保存導(dǎo)航位置或存儲(chǔ)你希望在整個(gè) web 應(yīng)用程序中使用但你不希望使用 cookie 的一些特定信息(例如用戶名稱或姓名)。 類(lèi)似的 API 還可以用于為個(gè)體會(huì)話存儲(chǔ)數(shù)據(jù)。 這些數(shù)據(jù)將在用戶瀏覽完離開(kāi)應(yīng)用程序或關(guān)閉瀏覽器之后自動(dòng)清除。
當(dāng)使用 web 存儲(chǔ) API 時(shí),你需要記住下列事項(xiàng):
核查瀏覽器支持功能
在使用web存儲(chǔ)API之前需要做的第一件事情是核查用戶的瀏覽器是否支持這一API:
function checkLocalStorageSupport() { try { return 'localStorage' in window && window['localStorage'] !== null; } catch (e) { return false; }}
你也許在上面的代碼片段中明顯地看出,web存儲(chǔ)使用一個(gè)名稱為localStorage
的對(duì)象,它是一個(gè)windowclass對(duì)象。 上面的代碼片段能夠核查localStorage
實(shí)際上是否是一個(gè)基于 window的對(duì)象,以及它能否返回true,以便應(yīng)用程序能夠充分利用本地存儲(chǔ)API。
添加和返回?cái)?shù)據(jù)
從localStorage
對(duì)象中添加和返回?cái)?shù)據(jù)與調(diào)用由本地存儲(chǔ)規(guī)范實(shí)現(xiàn)的getter和 setter方法一樣簡(jiǎn)單。 使用localStorage
可以存儲(chǔ)任何類(lèi)型的數(shù)據(jù),但所有的數(shù)據(jù)必須以字符串的格式存儲(chǔ)于相應(yīng)的存儲(chǔ)區(qū)域。 這意味著在將數(shù)據(jù)發(fā)送到localStorage
之前或在使用它們之后有必要對(duì)它們進(jìn)行解析。 例如,為了存儲(chǔ)一個(gè) JavaScript 對(duì)象,必須使用JSON 并且在檢索數(shù)據(jù)之前調(diào)用 stringify()
而在檢索數(shù)據(jù)之后調(diào)用parse()
。 在從localStorage
對(duì)象中檢索到數(shù)值變量之后,對(duì)它們進(jìn)行解析也是如此。
在本范例中,已經(jīng)建立一個(gè)單一的表單輸入,這樣,當(dāng)用戶點(diǎn)擊Submit時(shí),相應(yīng)的數(shù)據(jù)將存儲(chǔ)到本地高速緩存區(qū)域。 當(dāng)加載頁(yè)面時(shí),如果數(shù)據(jù)已經(jīng)存儲(chǔ),則頁(yè)面將通過(guò)一個(gè)歡迎窗口顯示已存儲(chǔ)的信息。 下面是當(dāng)用戶點(diǎn)擊 Submit時(shí)調(diào)用的函數(shù):
function onClick(){ if(checkLocalStorageSupport) { window.localStorage.setItem("name",document.getElementById("name").value); }}
該函數(shù)使用localStorage
對(duì)象中的 setItem
方法,然后使用相應(yīng)表單中的值來(lái)填充存儲(chǔ)高速緩存區(qū)。 當(dāng)加載頁(yè)面時(shí),應(yīng)用程序利用一個(gè) onLoad
函數(shù)來(lái)核查相應(yīng)的數(shù)據(jù)是否已經(jīng)位于本地高速緩存區(qū)中并且將添加一個(gè)歡迎窗口。
function onLoad(){ if(checkLocalStorageSupport) { var name = window.localStorage.getItem("name"); if(name != null) { window.document.getElementById("divName").innerHTML = "Welcome back " + name; } }}
清除數(shù)據(jù)
盡管用戶能夠使用瀏覽器在任何時(shí)間刪除localStorage
數(shù)據(jù),但為他們提供從應(yīng)用程序自身刪除數(shù)據(jù)的選項(xiàng)也是合理的。localStorage
提供一個(gè)正好能夠?qū)崿F(xiàn)這一目的 clear()
方法。 當(dāng)用戶在你的應(yīng)用程序中點(diǎn)擊一個(gè)復(fù)位按鈕時(shí),將觸發(fā)下面代碼。 如果你希望從存儲(chǔ)區(qū)中移除一個(gè)特定條目,則你可以使用removeItem()
方法從本地存儲(chǔ)區(qū)中刪除一個(gè)單一key。
function onReset(){ if(checkLocalStorageSupport()) { window.localStorage.clear(); }}
處理變更
Web存儲(chǔ) API 還包含一種偵聽(tīng)和響應(yīng)任何本地存儲(chǔ)變更的方法。 通過(guò)添加一個(gè)事件偵聽(tīng)程序以及偵聽(tīng)一個(gè)storage
事件,應(yīng)用程序能夠?qū)?code>localStorage變更進(jìn)行響應(yīng)。 事件中的數(shù)據(jù)包含已更改的key的名稱、新值、老值(如果有的話)以及調(diào)用該API的頁(yè)面的URL。 可以利用下面的方式實(shí)現(xiàn)localStorage API的規(guī)范要求,即發(fā)起事件的會(huì)話將不能看到該事件的觸發(fā)。 這是因?yàn)橐?guī)范要求該事件只能針對(duì)其它、而不是更改存儲(chǔ)的標(biāo)簽或會(huì)話進(jìn)行觸發(fā)。
為了偵聽(tīng)存儲(chǔ)事件,首先需要做的事情是添加事件偵聽(tīng)程序:
window.addEventListener("storage",onStorageChange);
然后建立 onStorageChange
事件來(lái)處理存儲(chǔ)事件。
function onStorageChange(e) { if(e.key == "name") { alert(e.newValue + ' just added their name to local storage'); } }
另外,有一個(gè)創(chuàng)建數(shù)據(jù)的類(lèi)似API,它只對(duì)個(gè)體會(huì)話持續(xù)有效。 通過(guò)使用sessionStorage
對(duì)象,而不是localStorage
對(duì)象,當(dāng)用戶離開(kāi)頁(yè)面時(shí),任何保存的數(shù)據(jù)將被自動(dòng)清除。 事實(shí)上,API 具有完全相同的方法,因此你可以仔細(xì)檢查并且利用sessionStorage
替換localStorage
,然后本地?cái)?shù)據(jù)將被保存直到用戶關(guān)閉他們的瀏覽器或其中包含應(yīng)用程序的標(biāo)簽。
有時(shí)在用戶的機(jī)器中僅僅存儲(chǔ)一些數(shù)據(jù)是不能滿足要求的。 在許多情形下,整個(gè)應(yīng)用程序必須離線運(yùn)行,而不僅僅存儲(chǔ)一些數(shù)據(jù)。 對(duì)于這種使用情形,HTML5 包含了在用戶機(jī)器上高速緩存文件和資源的功能,以便瀏覽器在沒(méi)有因特網(wǎng)連接的情形下訪問(wèn)它們。 這意味著構(gòu)成web應(yīng)用程序的圖像、JavaScript 文件、 HTML 文件、 CSS文件等大量數(shù)據(jù)能夠本地存儲(chǔ),甚至在沒(méi)有因特網(wǎng)連接的情形下能夠?qū)λ鼈冞M(jìn)行訪問(wèn)。 這一功能的關(guān)鍵是建立一個(gè)高速緩存的Manifest文件。
使用 manifest文件
Manifest文件是頁(yè)面的根 HTML標(biāo)簽的新manifest
屬性的一個(gè)組成部分。 它是一個(gè)位于web服務(wù)器上的文件,它能夠列出瀏覽器應(yīng)該下載和保存以便以后使用的所有文件。 它具有一個(gè) .manifest 擴(kuò)展名并且其唯一主要的gotcha 是相應(yīng)的web 服務(wù)器必須支持 .manifest mime類(lèi)型,因此,應(yīng)該確保駐留應(yīng)用程序的該 web 服務(wù)器能夠正確提供.manifest 文件。
Manifest文件具有一個(gè)基本架構(gòu)。 每個(gè)manifest 文件以 CACHE MANIFEST
開(kāi)頭,并且從這里開(kāi)始,列出所有瀏覽器需要高速緩存的、用于離線訪問(wèn)的文件。 下面是一個(gè)簡(jiǎn)單范例,它能夠存儲(chǔ)一些JavaScript、一個(gè) CSS 文件、一些圖像和 相應(yīng)的HTML頁(yè)面:
CACHE MANIFESTstyle.cssofflinescript.jsimages/dreamweaver_logo.pngimages/edge_logo.png
相應(yīng)的路徑均與用戶正在訪問(wèn)的HTML頁(yè)面相關(guān)。 當(dāng)創(chuàng)建高速緩存manifest文件時(shí),你必須了解一些其它選項(xiàng)。 其中一個(gè)選項(xiàng)是絕不能高速緩存的文件的情形。 也許只有能夠在線獲得的動(dòng)態(tài)腳本或某些內(nèi)容才是有意義的。 高速緩存manifest文件能夠劃分為告知瀏覽器如何對(duì)某些內(nèi)容進(jìn)行響應(yīng)的區(qū)段(section)。 通過(guò)創(chuàng)建一個(gè) NETWORK
和列出那些絕不能高速緩存的文件,瀏覽器一定能夠忽略這些文件并且讓人們決不能離線獲得它們。
另一個(gè)情形是當(dāng)用戶試圖訪問(wèn)一個(gè)沒(méi)有高速緩存的頁(yè)面或某些應(yīng)該高速緩存但卻沒(méi)有正確保存的內(nèi)容時(shí)的情形。 高速緩存 manifest API 能夠提供一個(gè)FALLBACK
區(qū)段(section),它指向一個(gè)在上述用例中加載的頁(yè)面。 因此,當(dāng)用戶試圖訪問(wèn)沒(méi)有保存的某些內(nèi)容時(shí),他們將看到一條關(guān)于離線提示的消息。 下面是一個(gè)理論上的包含這些區(qū)段(section)的高速緩存Manifest文件的大概架構(gòu):
CACHE MANIFESTNETWORK:my_dynamic_script.cgiFALLBACK:my_offline_message.htmlCACHE:style.cssofflinescript.jsimages/dreamweaver_logo.pngimages/edge_logo.png
在本例中,我提供了一個(gè)帶有一個(gè)外部JavaScript 頁(yè)面和外部 CSS頁(yè)面的HTML頁(yè)面。 該HTML頁(yè)面能夠顯示一些描述一個(gè)Adobe徽標(biāo)的文本,并且當(dāng)你點(diǎn)擊相應(yīng)的圖像時(shí),JavaScript 將會(huì)為另一個(gè)徽標(biāo)換出相應(yīng)的圖像和文本。 下面是相應(yīng)的HTML代碼,緊跟其后的是JavaScript函數(shù):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" manifest="cache.manifest"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Adobe Logos</title><script src="offlinescript.js"></script><link href="style.css" rel="stylesheet" /></head><body><div id="textContent">This is the Edge logo:</div><br /><img id="logo" name="logo" src="images/edge_logo.png" onclick="onLogoClick();" /><p class="small">Click on the logo to swap it out.</p></body></html>// JavaScript Documentfunction onLogoClick(e){ var currentContent = window.document.getElementById("textContent").innerHTML; if(currentContent == "This is the Edge logo:") { window.document.getElementById("textContent").innerHTML = "This is the Dreamweaver logo:"; window.document.logo.src = "images/dreamweaver_logo.png"; } else { window.document.getElementById("textContent").innerHTML = "This is the Edge logo:"; window.document.logo.src = "images/edge_logo.png"; }}
所有這些代碼的關(guān)鍵部分是帶有 manifest
屬性的HTML標(biāo)簽。 它是指向我在上面引用的我的cache.manifest 文件。 該manifest文件能夠指示瀏覽器下載列表中給出的所有文件。 不管用戶在瀏覽這些文件時(shí)是否下載它們,相應(yīng)的瀏覽器將自動(dòng)下載manifest文件中包含的所有文件。 這意味著兩個(gè)圖像都將被保存以便離線訪問(wèn),即使第二個(gè)圖像直到我與相應(yīng)內(nèi)容互動(dòng)時(shí)才加載到頁(yè)面。 因此,只需加載該頁(yè)面一次,我即可以在離線情形下完全與它進(jìn)行互動(dòng),并且兩個(gè)圖像均會(huì)旋轉(zhuǎn)。
了解事件
需要略微提到的、但卻是非常重要的離線訪問(wèn)的最后部分是發(fā)生在高速緩存過(guò)程中的事件。 當(dāng)瀏覽器遇到 manifest 屬性,它將在window.applicationCache 對(duì)象中觸發(fā)一系列事件。 第一個(gè)發(fā)生的事件是觸發(fā)一個(gè)checking事件。 該事件可以確定需要利用這一特別的高速緩存文件進(jìn)行哪些操作。 Google Chrome 開(kāi)發(fā)人員工具可以在高速緩存區(qū)保存數(shù)據(jù)時(shí)能夠全面地核查發(fā)生的事件(參見(jiàn)圖1)。
如果這是用戶第一次訪問(wèn)該網(wǎng)站,則將觸發(fā)一個(gè)下載事件并且相應(yīng)的web瀏覽器將仔細(xì)查看并且下載manifest文件中包含的所有資源。 它能夠讀取相應(yīng)的manifest文件,確定它需要下載多少文件,然后以progress事件的形式為每個(gè)文件回送狀態(tài)更新信息。 Progress 事件包含一個(gè)已加載的變量和一個(gè)總變量,這樣開(kāi)發(fā)人員能夠確定高速緩存區(qū)已經(jīng)存儲(chǔ)的多少內(nèi)容。
function onProgress(e){ var content = window.document.getElementById("loadedInfo").innerHTML; window.document.getElementById("loadedInfo").innerHTML = content + '<br /> Loaded file ' + e.loaded + ' of ' + e.total;}
當(dāng)完成所有文件的保存操作時(shí),瀏覽器將觸發(fā)一個(gè)cached事件通知開(kāi)發(fā)人員所有用于離線使用的文件已經(jīng)成功保存。
在后面用戶訪問(wèn)頁(yè)面的任何時(shí)刻,瀏覽器將核查看一看在manifest 文件中是否有內(nèi)容發(fā)生改變。 如果沒(méi)有,它將觸發(fā)一個(gè) noupdate
事件,然后繼續(xù)運(yùn)行。 如果其中有內(nèi)容發(fā)生改變,則它將經(jīng)歷與上面完全相同的過(guò)程;它將觸發(fā)一個(gè)包含一系列progress
事件的downloading
事件,直到相應(yīng)的文件全部更新完畢。 當(dāng)該事件發(fā)生時(shí),而不是觸發(fā)一個(gè)cached
事件,瀏覽器將觸發(fā)一個(gè)updateready
事件表示所有的文件已經(jīng)更新并且可以離線使用。
最后一個(gè)令人發(fā)愁的事件是error
事件,它將在應(yīng)用程序出現(xiàn)故障時(shí)觸發(fā)。 這些故障可能是文件不能正確加載,瀏覽器不能訪問(wèn)cache manifest文件,或manifest列出的一個(gè)或多個(gè)文件不存在等。 為了捕捉這些故障,只需為error
事件添加一個(gè)事件偵聽(tīng)程序。
window.applicationCache.addEventListener("error",onError);function onError(e){ window.document.getElementById("loadedInfo").innerHTML = "Something went wrong while saving the files for offline use.";}
HTML5引入的最后一個(gè)存儲(chǔ)類(lèi)型是目前最處于不斷變化之中的類(lèi)型。 最初有一個(gè) Web SQL規(guī)范,但它現(xiàn)在已經(jīng)不再使用。 現(xiàn)在,大多數(shù)人的精力已經(jīng)轉(zhuǎn)移到Indexed Database API 上,并且似乎這將是在關(guān)系型數(shù)據(jù)庫(kù)中存儲(chǔ)信息的出路。 Firefox和 Chrome 均支持IndexedDB,但由于相應(yīng)的規(guī)范和支持功能均處于不斷變化之中,所以它超出本文的討論范圍。 在將來(lái)某個(gè)時(shí)候,當(dāng)這一狀態(tài)改變時(shí),我將進(jìn)行相應(yīng)的更新。
在本文中,我探索了兩種本地存儲(chǔ)信息的主要方法。 第一種方法以web存儲(chǔ)API的形式提供一些非?;镜臄?shù)據(jù)存儲(chǔ)技術(shù)。 利用這一方法,你可以在會(huì)話之外或?yàn)橐粋€(gè)單一會(huì)話保存名稱值對(duì)(name value pair)。 第二種方法,即應(yīng)用程序cache manifest,允許開(kāi)發(fā)人員在本地機(jī)器上保存所有文件,以便可以在離線情形下訪問(wèn)它們。
關(guān)于HTML5存儲(chǔ)API的更多信息,參見(jiàn)下列資源:
本產(chǎn)品經(jīng) Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License 許可。Adobe 提供超出該許可范圍、與本產(chǎn)品包含的代碼示例相關(guān)的權(quán)限。
聯(lián)系客服