第23 離線應(yīng)用與客戶端存儲(chǔ)
1. 離線檢測(cè)
(1)
navigator.onLine 屬性,這個(gè)屬性值為true表示設(shè)備能上網(wǎng),值為false表示設(shè)備離線。
(2)兩個(gè)事件: online 和 offline。當(dāng)網(wǎng)絡(luò)從離線變?yōu)樵诰€或者從在線變?yōu)殡x線時(shí),分別觸發(fā)這兩個(gè)事件。這兩個(gè)事 件在 window 對(duì)象上觸發(fā)。
2. 應(yīng)用緩存
(1) HTML5 的
應(yīng)用緩存(application cache),或者簡(jiǎn)稱為 appcache,是專門為開發(fā)離線 Web 應(yīng)用而設(shè)計(jì) 的。Appcache 就是從瀏覽器的緩存中分出來的一塊緩存區(qū)。要想在這個(gè)緩存中保存數(shù)據(jù),可以使用一個(gè)描述文件(manifest file),列出要下載和緩存的資源。
(2) 要將描述文件與頁面關(guān)聯(lián)起來,可以在<html>中的 manifest 屬性中指定這個(gè)文件的路徑<htmlmanifest="/offline.manifest">
這個(gè)文件的 MIME 類型必須是 text/cache-manifest1。
(3)applicationCache 對(duì)象,這個(gè)對(duì)象有一個(gè)status 屬性,屬性的值是常量,表示應(yīng)用緩存的如下當(dāng)前狀態(tài)。
- 0:無緩存,即沒有與頁面相關(guān)的應(yīng)用緩存。
- 1:閑置,即應(yīng)用緩存未得到更新。
- 2:檢查中,即正在下載描述文件并檢查更新。
- 3:下載中,即應(yīng)用緩存正在下載描述文件中指定的資源。
- 4:更新完成,即應(yīng)用緩存已經(jīng)更新了資源,而且所有資源都已下載完畢,可以通過 swapCache()來使用了。
- 5:廢棄,即應(yīng)用緩存的描述文件已經(jīng)不存在了,因此頁面無法再訪問應(yīng)用緩存。
(4) 應(yīng)用緩存還有很多相關(guān)的事件,表示其狀態(tài)的改變。以下是這些事件。
- checking:在瀏覽器為應(yīng)用緩存查找更新時(shí)觸發(fā)。
- error:在檢查更新或下載資源期間發(fā)生錯(cuò)誤時(shí)觸發(fā)。
- noupdate:在檢查描述文件發(fā)現(xiàn)文件無變化時(shí)觸發(fā)。
- downloading:在開始下載應(yīng)用緩存資源時(shí)觸發(fā)。
- progress:在文件下載應(yīng)用緩存的過程中持續(xù)不斷地觸發(fā)。
- updateready:在頁面新的應(yīng)用緩存下載完畢且可以通過 swapCache()使用時(shí)觸發(fā)。
- cached:在應(yīng)用緩存完整可用時(shí)觸發(fā)
(5)
update()方法也可以 手工干預(yù),讓應(yīng)用緩存為檢查更新而觸發(fā)上述事件。applicationCache.update();
(6) 如果觸發(fā)了updateready 事件,則說明新版本的應(yīng)用緩存已經(jīng)可用,而此時(shí)你需 要調(diào)用swapCache()來啟用新應(yīng)用緩存。
EventUtil.addHandler(applicationCache, "updateready", function(){
applicationCache.swapCache();
});
3. 數(shù)據(jù)存儲(chǔ)
(1) Cookie
HTTP Cookie,通常直接叫做 cookie,最初是在客戶端用于存儲(chǔ)會(huì)話信息的。該標(biāo)準(zhǔn)要求服務(wù)器對(duì)任意 HTTP 請(qǐng)求發(fā)送Set-CookieHTTP 頭作為響應(yīng)的一部分,其中包含會(huì)話信息。- 瀏覽器會(huì)存儲(chǔ)這樣的會(huì)話信息,并在這之后,通過為每個(gè)請(qǐng)求添加
Cookie HTTP 頭將信 息發(fā)送回服務(wù)器。
* 限制
cookie 在性質(zhì)上是綁定在
特定的域名下的。當(dāng)設(shè)定了一個(gè) cookie 后,再給創(chuàng)建它的域名發(fā)送請(qǐng)求時(shí), 都會(huì)包含這個(gè) cookie。這個(gè)限制確保了儲(chǔ)存在 cookie 中的信息只能讓批準(zhǔn)的接受者訪問,而無法被其他 域訪問。
- 每個(gè)域的 cookie 總數(shù)是有限的,不過瀏覽器之間各有不同。
- 瀏覽器中對(duì)于 cookie 的尺寸也有限制。大多數(shù)瀏覽器都有大約 4096B(加減 1)的長(zhǎng)度限制。
*cookie 的構(gòu)成
cookie 由瀏覽器保存的以下幾塊信息構(gòu)成:
- 名稱(name):一個(gè)唯一確定 cookie 的名稱。cookie 名稱是不區(qū)分大小寫的,所以 myCookie 和 MyCookie 被認(rèn)為是同一個(gè) cookie。然而,實(shí)踐中最好將 cookie 名稱看作是區(qū)分大小寫的,因?yàn)槟承┓?wù)器會(huì)這樣處理 cookie。cookie 的名稱必須是經(jīng)過 URL 編碼的。
- 值:儲(chǔ)存在 cookie 中的字符串值。值必須被 URL 編碼。
- 域(domain):cookie 對(duì)于哪個(gè)域是有效的。所有向該域發(fā)送的請(qǐng)求中都會(huì)包含這個(gè) cookie 信息。這個(gè)值
可以包含子域(subdomain,如 www.wrox.com),也可以不包含它(如.wrox.com,則對(duì)于 wrox.com的所有子域都有效)。如果沒有明確設(shè)定,那么這個(gè)域會(huì)被認(rèn)作來自設(shè)置 cookie 的那個(gè)域。- 路徑(path):對(duì)于指定域中的那個(gè)路徑,應(yīng)該向服務(wù)器發(fā)送 cookie。例如,你可以指定 cookie 只有從 http://www.wrox.com/books/ 中才能訪問,那么 http://www.wrox.com 的頁面就不會(huì)發(fā)送 cookie 信息,即使請(qǐng)求都是來自同一個(gè)域的。
- 失效時(shí)間(expires):表示 cookie 何時(shí)應(yīng)該被刪除的時(shí)間戳(也就是,何時(shí)應(yīng)該停止向服務(wù)器發(fā)送這個(gè)
cookie)。默認(rèn)情況下,瀏覽器會(huì)話結(jié)束時(shí)即將所有 cookie 刪除;不過也可以自己設(shè)置刪除時(shí)間。 這個(gè)值是個(gè) GMT 格式的日期(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定應(yīng)該刪除 cookie 的準(zhǔn)確時(shí)間。因此,cookie 可在瀏覽器關(guān)閉后依然保存在用戶的機(jī)器上。如果你設(shè)置的失 效日期是個(gè)以前的時(shí)間,則 cookie 會(huì)被立刻刪除。- 安全標(biāo)志(secure):指定后,cookie 只有在使用 SSL 連接的時(shí)候才發(fā)送到服務(wù)器。例如,cookie 信息只 能發(fā)送給 https://www.wrox.com,而 http://www.wrox.com 的請(qǐng)求則不能發(fā)送 cookie
每一段信息都作為 Set-Cookie 頭的一部分,使用分號(hào)加空格分隔每一段
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
*JavaScript 中的 cookie
document. cookie 屬性。
- 當(dāng)用來獲取屬性值時(shí), document.cookie 返回當(dāng)前頁面可用的(根據(jù) cookie 的域、路徑、失效時(shí)間和安全設(shè)置)所有 cookie 的字符串,一系列由分號(hào)隔開的名值對(duì)兒.
- 當(dāng)用于設(shè)置值的時(shí)候,document.cookie 屬性可以設(shè)置為一個(gè)新的 cookie 字符串。這個(gè) cookie 字
符串會(huì)被解釋并添加到現(xiàn)有的 cookie 集合中。設(shè)置 document.cookie 并不會(huì)覆蓋 cookie,除非設(shè)置的cookie 的名稱已經(jīng)存在。
*子 cookie
子 cookie 是存放在單個(gè) cookie 中的更小段的數(shù)據(jù)。也就是使用 cookie 值來存儲(chǔ)多個(gè)名稱值對(duì) 兒。子 cookie 最常見的的格式如下所示。
name=name1=value1&name2=value2&name3=value3&name4=value4
(2) IE用戶數(shù)據(jù)
在 IE5.0 中,微軟通過一個(gè)自定義行為引入了持久化用戶數(shù)據(jù)的概念。用戶數(shù)據(jù)允許每個(gè)文檔最多 128KB 數(shù)據(jù),每個(gè)域名最多 1MB 數(shù)據(jù)。要使用
持久化用戶數(shù)據(jù),首先必須如下所示,使用 CSS 在某個(gè) 元素上指定userData 行為:
<div style="behavior:url(#default#userData)" id="dataStore"></div>
- 使用
setAttribute()方法保存數(shù)據(jù)。- 為了將數(shù)據(jù)提交到瀏覽器緩存中,還必須調(diào)用
save()方法并告訴它要保存到的數(shù)據(jù)空間的名字。數(shù)據(jù)空間名字可以完全任意,僅用于區(qū)分不同的數(shù)據(jù)集。
var dataStore = document.getElementById("dataStore");
dataStore.setAttribute("name", "Nicholas");
dataStore.setAttribute("book", "Professional JavaScript");
dataStore.save("BookInfo");
- 下一次頁面載入之后,可以使用
load()方法指定 同樣的數(shù)據(jù)空間名稱來獲取數(shù)據(jù)。getAttribute()調(diào)用了不存在的名稱或者是尚未載入的名 稱,則返回 null。
dataStore.load("BookInfo");
alert(dataStore.getAttribute("name")); //"Nicholas"
alert(dataStore.getAttribute("book")); //"Professional JavaScript"
- 通過
removeAttribute()方法明確指定要?jiǎng)h除某元素?cái)?shù)據(jù),只要指定屬性名稱。刪除之后, 必須像下面這樣再次調(diào)用 save()來提交更改。
dataStore.removeAttribute("name");
dataStore.removeAttribute("book");
dataStore.save("BookInfo");
(3) Web存儲(chǔ)機(jī)制
*Storage 類型
- clear(): 刪除所有值;Firefox 中沒有實(shí)現(xiàn) 。
- getItem(name):根據(jù)指定的名字 name 獲取對(duì)應(yīng)的值。
- key(index):獲得 index 位置處的值的名字。
- removeItem(name):刪除由 name 指定的名值對(duì)兒。
- setItem(name, value):為指定的 name 設(shè)置一個(gè)對(duì)應(yīng)的值。
*sessionStorage 對(duì)象
sessionStorage對(duì)象存儲(chǔ)特定于某個(gè)會(huì)話的數(shù)據(jù),也就是該數(shù)據(jù)只保持到瀏覽器關(guān)閉。
- sessionStorage 對(duì)象其實(shí)是 Storage 的一個(gè)實(shí)例,所以可以使用 setItem()或者直接設(shè)。
- sessionStorage 中有數(shù)據(jù)時(shí),可以使用 getItem()或者通過直接訪問屬性名來獲取數(shù)據(jù)。
- 可以通過結(jié)合 length 屬性和 key()方法來迭代 sessionStorage 中的值。
- 可以使用 for-in 循環(huán)來迭代 sessionStorage 中的值。
- 可以使用 delete 操作符刪除對(duì)象屬性,也可調(diào)用 removeItem()方法。
*globalStorage 對(duì)象
globalStorage這個(gè)對(duì)象的目的是跨越會(huì)話存儲(chǔ)數(shù)據(jù),但有特定的訪問限制。要使用 globalStorage,首先要指定哪些域可以訪問該數(shù)據(jù)??梢酝ㄟ^方括號(hào)標(biāo)記使用屬性來實(shí)現(xiàn)。
- 對(duì) globalStorage 空間的訪問,是依據(jù)發(fā)起請(qǐng)求的頁面的域名、協(xié)議和端口來限制的。
- 如果你事先不能確定域名,那么使用
location.host作為屬性名比較安全。- 如果不使用 removeItem()或者 delete 刪除,或者用戶未清除瀏覽器緩存,存儲(chǔ)在 globalStorage 屬性中的數(shù)據(jù)會(huì)一直保留在磁盤上。
*localStorage 對(duì)象
不能給localStorage指定任何訪問規(guī)則;規(guī)則事先就設(shè)定好了。要訪問同一個(gè) localStorage 對(duì)象,頁面必須來自同一個(gè)域名(子域名無效),使用同一種 協(xié)議,在同一個(gè)端口上。這相當(dāng)于 globalStorage[location.host]。
*storage 事件
這個(gè)事件的
event 對(duì)象有以下屬性。
(1) domain:發(fā)生變化的存儲(chǔ)空間的域名。
(2) key:設(shè)置或者刪除的鍵名。
(3) newValue:如果是設(shè)置值,則是新值;如果是刪除鍵,則是 null。
(4) oldValue:鍵被更改之前的值。
(4) IndexedDB
*數(shù)據(jù)庫
IndexedDB 最大的特色是使用對(duì)象保存數(shù)據(jù),而不是使用表來保存數(shù)據(jù)。一個(gè) IndexedDB 數(shù)據(jù)庫,就是 一組位于相同命名空間下的對(duì)象的集合。
var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
- 把要打開的數(shù)據(jù)庫名傳給
indexDB.open()。如果傳入的 數(shù)據(jù)庫已經(jīng)存在,就會(huì)發(fā)送一個(gè)打開它的請(qǐng)求;如果傳入的數(shù)據(jù)庫還不存在,就會(huì)發(fā)送一個(gè)創(chuàng)建并打開 它的請(qǐng)求??傊?調(diào)用 indexDB.open()會(huì)返回一個(gè) IDBRequest 對(duì)象,在這個(gè)對(duì)象上可以添加 onerror 和 onsuccess 事件處理程序。
var request, database;
request = indexedDB.open("admin");
request.onerror = function(event){
alert("Something bad happened while trying to open: " +
event.target.errorCode);
};
request.onsuccess = function(event){
database = event.target.result;
};
(1)
event.target.result中將有一個(gè)數(shù)據(jù)庫實(shí)例對(duì)象(IDBData- base),這個(gè)對(duì)象會(huì)保存在 database 變量中。
(2) 如果發(fā)生了錯(cuò)誤,那event.target.errorCode中將 保存一個(gè)錯(cuò)誤碼,表示問題的性質(zhì)。
- 默認(rèn)情況下,IndexedDB 數(shù)據(jù)庫是沒有版本號(hào)的,最好一開始就為數(shù)據(jù)庫指定一個(gè)版本號(hào)。為此, 可以調(diào)用
setVersion()方法,傳入以字符串形式表示的版本號(hào)。