本文要介紹的,是 HTML5 離線網(wǎng)絡(luò)應(yīng)用程序的特性,離線網(wǎng)絡(luò)應(yīng)用程序在 W3C 中的實(shí)際名稱是 "Offline Web applications" ,也稱離線緩存。當(dāng)用戶打開(kāi)瀏覽器時(shí),瀏覽器會(huì)將一個(gè)列表中指定的資源都下載并儲(chǔ)存在本地。下次當(dāng)用戶再訪問(wèn)這個(gè)網(wǎng)絡(luò)程序時(shí),瀏覽器會(huì)自動(dòng)引用本地緩存中相應(yīng)的文件,而不會(huì)再?gòu)木W(wǎng)絡(luò)下載這些資源。不管離線網(wǎng)絡(luò)應(yīng)用程序是否專為 Web Apps 而設(shè),但這對(duì)于 Web Apps 來(lái)說(shuō)無(wú)疑是個(gè)非常實(shí)用的特性,它使到 Web Apps 相對(duì)于原生 Apps 的一個(gè)重要劣勢(shì) —— 高度依賴網(wǎng)絡(luò),得以大大減緩。開(kāi)發(fā)者可以利用這個(gè)特性把 Web Apps 中的元素緩存到本地端,使到 Web Apps 可以脫機(jī)工作,即使是需要聯(lián)網(wǎng)工作的 Apps ,也可以緩存部分文件到本地端,減少帶寬占用,這樣 Web Apps 相對(duì)于原生 Apps 就更加具有優(yōu)勢(shì)了。下面正式開(kāi)始介紹這個(gè)特性。
離線網(wǎng)絡(luò)應(yīng)用程序的核心是一個(gè) content type (內(nèi)容類型) 為 cache-manifest 的文本文件。這個(gè)文件保存了應(yīng)用程序中需要離線存儲(chǔ)的文件(HTML, CSS, JavaScript, 圖片等)。舉一個(gè)簡(jiǎn)單的例子,若一個(gè)應(yīng)用程序由以下文件組成:
index.html 為主頁(yè),其他文件是主頁(yè)中引用的資源,如果我們需要離線緩存這個(gè)應(yīng)用程序,需要在 index.html 的同級(jí)目錄下增加一個(gè) manifest 文件,并命名為 .manifest 后綴文件,而文件中的內(nèi)容可以這樣編寫(xiě):
1 2 3 4 5 | CACHE MANIFEST ./index.html ./demo.css ./demo.js ./logo.png |
可以看出,以上文件的路徑是相對(duì)路徑,這里是相對(duì)于 manifest 文件而言的。當(dāng)然,你也可以使用絕對(duì)路徑,這并不影響 manifest 的使用。
manifest 文件的編寫(xiě)很簡(jiǎn)單,但啟用離線緩存還需一些步驟。首先是需要把 manifest 文件和應(yīng)用程序關(guān)聯(lián)起來(lái),即把頁(yè)面指向緩存名單,方法是為 html 標(biāo)簽指定 manifest 值,例如:
1 | < html manifest = "demo.manifest" > |
其中 demo.manifest 是例子中編寫(xiě)的 manifest 列表文件。
小提示:若應(yīng)用程序中有多個(gè)頁(yè)面,則每個(gè)頁(yè)面都需要與 manifest 關(guān)聯(lián)起來(lái)。
接著,開(kāi)發(fā)者必須給 manifest 文件指定 text/cache-manifest 內(nèi)容類型,這樣瀏覽器才能識(shí)別它。關(guān)于具體的方法,如果你的服務(wù)器支持 .htaccess ,可以在 .htaccess 中寫(xiě)入以下語(yǔ)句:
1 | AddType text/cache-manifest .manifest |
這樣做可以把 text/cache-manifest 的 MIME 類型和 .manifest 文件關(guān)聯(lián)起來(lái)。當(dāng)然,可能開(kāi)發(fā)者并沒(méi)有 .htaccess 的編寫(xiě)權(quán)限,但實(shí)際上,開(kāi)發(fā)者可以使用另外一些更實(shí)用的方法達(dá)到目的。因?yàn)樵趯?shí)際的項(xiàng)目開(kāi)發(fā)中,應(yīng)用程序中的文件數(shù)量不會(huì)只像上例中僅有的 4 個(gè),如果有很多的文件需要緩存,每個(gè)文件都需要手動(dòng)寫(xiě)入 manifest 文件實(shí)在比較費(fèi)時(shí),更麻煩的是,每次改動(dòng)這些文件都必須相應(yīng)地改動(dòng) manifest 文件,因此 Kayo 更加建議開(kāi)發(fā)者使用后臺(tái)腳本直接獲取需要緩存的文件并寫(xiě)入一個(gè) manifest 列表,Kayo 熟悉的后臺(tái)腳本是 PHP ,在 PHP 中,可以直接在代碼中設(shè)置 MIME 類型,這樣就不需要配置 Web 服務(wù)來(lái)完成對(duì) manifest 的支持了。至于具體如何使用 PHP 編寫(xiě) manifest ,會(huì)在下面詳細(xì)介紹。
關(guān)于離線網(wǎng)絡(luò)應(yīng)用程序在現(xiàn)代瀏覽器中都已經(jīng)實(shí)現(xiàn)完整的支持,IE 則完全不支持。具體如下:
Chrome 4+ , Firefox 3.5+ , Safari 4+ 和 Opera 10.6+
默認(rèn)情況下,開(kāi)發(fā)者會(huì)為應(yīng)用程序中所有文件指定緩存,但實(shí)際上,仍有一些資源可能需要強(qiáng)制取消緩存,即必須訪問(wèn)網(wǎng)絡(luò)資源,離線時(shí)該資源不可用。為元素強(qiáng)制取消緩存可以在 manifest 文件中使用關(guān)鍵字 NETWORK: 。在 NETWORK: 關(guān)鍵字下添加文件的列表,這些文件會(huì)強(qiáng)制取消緩存,而這個(gè)文件列表就稱為白名單 (Whitelist) 。實(shí)際上,對(duì)于需要緩存的資源也是有關(guān)鍵字的,這個(gè)關(guān)鍵字是 EXPLICIT: ,但所有資源列表的開(kāi)頭如果沒(méi)有添加關(guān)鍵字,默認(rèn)都會(huì)被認(rèn)為在 EXPLICIT: 關(guān)鍵字的列表下,因此需要緩存的資源列表開(kāi)頭可以忽略不寫(xiě)關(guān)鍵字(如上例)。
例如,對(duì)上例進(jìn)行擴(kuò)展,把 logo.png 添加到白名單,可以這樣編寫(xiě) manifest 文件。
1 2 3 4 5 6 7 | CACHE MANIFEST ./index.html ./demo.css ./demo.js NETWORK: ./logo.png |
備選名單是對(duì)于白名單的補(bǔ)充,在白名單中,沒(méi)有聯(lián)網(wǎng)時(shí),資源不能加載,這樣會(huì)導(dǎo)致頁(yè)面出現(xiàn)錯(cuò)誤,如上例中,"logo.png" 被設(shè)置為白名單,如果用戶離線瀏覽該應(yīng)用程序,會(huì)出現(xiàn)一個(gè)損壞的圖片鏈接,為了避免這種情況,可以使用 FALLBACK: 指定一個(gè)備選名單,備選名單中需要為一個(gè)資源準(zhǔn)備兩個(gè)文件,若能正常聯(lián)網(wǎng),會(huì)引用第一個(gè)文件,離線時(shí)則引用第二個(gè)文件,關(guān)于這個(gè)特點(diǎn),有一個(gè)很實(shí)用的應(yīng)用 —— 表明程序是離線工作還是聯(lián)網(wǎng)工作,在不同的狀態(tài)下引用不同的圖片即可方便地表明狀態(tài)。接下來(lái) Kayo 繼續(xù)擴(kuò)展上例,為 logo.png 指定一個(gè)離線時(shí)的替換圖片 backup.png 。
1 2 3 4 5 6 7 | CACHE MANIFEST ./index.html ./demo.css ./demo.js FALLBACK: ./logo.png ./backup.png |
似乎,經(jīng)過(guò)上面三個(gè)步驟后,應(yīng)用程序的離線緩存已經(jīng)很好的工作了。但有一個(gè)很重要的問(wèn)題仍需注意 —— 根據(jù)離線緩存的工作原理,當(dāng)用戶第一次使用應(yīng)用程序時(shí),瀏覽器會(huì)根據(jù) manifest 文件里的列表下載指定的資源,在下次使用該應(yīng)用程序時(shí),瀏覽器會(huì)自動(dòng)加載這些資源的副本,那么假如開(kāi)發(fā)者修改了程序,瀏覽器仍舊會(huì)加載本地的資源,因此我們需要給瀏覽器一個(gè)提示,程序已經(jīng)更新。
那么什么時(shí)候?yàn)g覽器會(huì)更新本地緩存呢?
只有當(dāng) manifest 修改后瀏覽器才會(huì)重新下載 manifest 中所有指定需要離線緩存的文件。而檢測(cè) manifest 是否修改過(guò)是對(duì) manifest 文件的內(nèi)容逐個(gè)字符進(jìn)行比較,包括注釋和空行。當(dāng)然,大多情況下,修改程序時(shí)并不會(huì)影響主要的文件,所以 manifest 中的文件列表也不會(huì)有改動(dòng),因此需要改動(dòng) manifest 以通知瀏覽器程序已更新的最好辦法是修改注釋,開(kāi)發(fā)者可以在 manifest 文件中添加一行注釋,比如說(shuō)是程序的版本號(hào),當(dāng)程序更新后同時(shí)修改 manifest 文件中這個(gè)版本號(hào),瀏覽器就會(huì)判定程序已經(jīng)更新,自動(dòng)重新下載所有需要離線緩存的資源。例如,把上例改成:
1 2 3 4 5 6 7 8 | CACHE MANIFEST # version 1.0 ./index.html ./demo.css ./demo.js FALLBACK: ./logo.png ./backup.png |
需要注意的是,"CACHE MANIFEST"是必要行,并且必須在 manifest 文件中的第一行。這樣下次更改程序后同時(shí)修改 manifest 的程序版本號(hào),瀏覽器就可以判定程序更新了。
當(dāng)然,為了能更精確地控制程序更新,最好是使用手動(dòng)方法更新緩存,離線網(wǎng)絡(luò)應(yīng)用程序規(guī)范中也提供了相應(yīng)的手動(dòng)更新緩存的方法。開(kāi)發(fā)者可以使用 window.applicationCache.update() 方法手動(dòng)更新緩存,為了更準(zhǔn)確地判斷是否需要更新,開(kāi)發(fā)者可以先檢測(cè) window.applicationCache.status 的值,若其值為 "UPDATEREADY" (即瀏覽器檢測(cè)到 manifest 已被修改), 可以調(diào)用 window.applicationCache.update() 方法更新緩存。例如:
1 2 3 | if ( window.applicationCache.status == window.applicationCache.UPDATEREADY ){ window.applicationCache.update(); } |
如上面所說(shuō),使用腳本編寫(xiě) manifest 文件可以同時(shí)解決 manifest 文件擴(kuò)展名與 MIME 類型關(guān)聯(lián)起來(lái),還可以自動(dòng)列出緩存文件。具體的編寫(xiě)如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | header( 'Content-Type: text/cache-manifest' ); echo "CACHE MANIFEST\n" ; $allHashes = "" ; // 創(chuàng)建一個(gè)字符串保存文件的 md5 值 $dir = new RecursiveDirectoryIterator( "." ); foreach ( new RecursiveIteratorIterator( $dir ) as $file ){ // 獲取當(dāng)前目錄并遍歷文件 if ( $file ->IsFile() && // 判斷獲取內(nèi)容為文件 $file ->getFilename() != "manifest.php" && // "manifest.php" 不緩存 $file ->getFilename() != "logo.png" && // 備選資源不緩存 $file ->getFilename() != "offline.png" && ! strpos ( $file , '/.' ) && substr ( $file ->getFilename(), 0, 1 ) != "." ){ echo "./" . $file ->getFilename() . "\n" ; $allHashes .= md5_file( $file ); // 把每一個(gè)緩存的文件的 md5 值連接起來(lái) } } echo "FALLBACK:\n" ; // 輸出備選名單 echo "./logo.png ./offline.png\n" ; echo "# " . md5( $allHases ) . "\n" ; // 把連接起來(lái)的 md5 值重新計(jì)算一個(gè) md5(因?yàn)檫B接所得的字符串過(guò)于冗長(zhǎng)) |
在這個(gè)腳本中,獲取了應(yīng)用程序的目錄并把目錄中的文件逐個(gè)添加到緩存列表,同時(shí)在這個(gè)過(guò)程中排除如 "manifest.php" 和備選資源等文件,并且,對(duì)每一個(gè)緩存的文件計(jì)算 md5 值,把這些值連接起來(lái)后重新計(jì)算一個(gè) md5 并輸出到一行注釋中,這樣緩存的文件只要有一個(gè)發(fā)生了改變,都會(huì)影響這行注釋,從而使到整個(gè) manifest 文件發(fā)生改變,這樣就并不需要在更新程序后手動(dòng)修改 manifest 文件??梢钥闯觯绻枰彺娴奈募芏鄷r(shí),該方法將會(huì)十分方便,并且更新程序后 manifest 文件也會(huì)被自動(dòng)修改。當(dāng)然,這只是使用腳本編寫(xiě) manifest 的其中一種方式,開(kāi)發(fā)者應(yīng)視具體情況而制定相應(yīng)的腳本。
在 “手動(dòng)更新緩存” 中,Kayo 提到了一個(gè)對(duì)象 window.applicationCache ,這個(gè)對(duì)象中包含了與離線網(wǎng)絡(luò)應(yīng)用程序相關(guān)的屬性、方法和事件,除了上面涉及到的 status 屬性和 update() 方法外,還有其他的相關(guān)內(nèi)容,下面開(kāi)始對(duì)它們進(jìn)行詳細(xì)介紹。
window.applicationCache 對(duì)象中具有一個(gè)屬性 status ,該狀態(tài)會(huì)根據(jù)當(dāng)前的緩存狀態(tài)顯示不同的值和狀態(tài)編號(hào),具體的屬性值如下:
接著,是相關(guān)的方法,具體如下:
除了屬性和方法,window.applicationCache 對(duì)象還會(huì)根據(jù)一些情況在其身上觸發(fā)一些事件,開(kāi)發(fā)者應(yīng)該了解這些事件,根據(jù)所觸發(fā)的事件,可以了解一個(gè)正在運(yùn)作的離線緩存的工作情況,然后根據(jù)不同的情況作出適當(dāng)?shù)奶幚怼?/p>
這里 Kayo 需要指出一點(diǎn),以上這些事件并不會(huì)在一個(gè)緩存過(guò)程中全部觸發(fā),如 error(前三種情況) , updateready , obsolete , noupdate , cached 均為一個(gè)緩存過(guò)程中的最終事件,在一次緩存過(guò)程中只會(huì)出現(xiàn)其中一個(gè)。又如 cached 事件,必須要有下載資源并且在完成后才觸發(fā),即第二次打開(kāi)頁(yè)面并且 manifest 沒(méi)有修改是不會(huì)觸發(fā) cached 事件(沒(méi)有下載資源),開(kāi)發(fā)者應(yīng)仔細(xì)注意每個(gè)事件的觸發(fā)時(shí)刻,判斷在不同情況下應(yīng)該利用何種事件。
這個(gè)例舉一個(gè)例子,使用 addEventListener 監(jiān)聽(tīng) cached 事件,檢測(cè)到 cached 事件發(fā)生時(shí)彈出提示,即代表離線資源已經(jīng)下載完成。
1 2 3 4 5 6 7 | if (window.applicationCache) { window.applicationCache.addEventListener( 'cached' , function (){ alert( 'cached' ); }, true ); } |
為了使讀者更好的理解離線網(wǎng)絡(luò)應(yīng)用程序的具體使用,這里會(huì)把上面逐步擴(kuò)展的例子寫(xiě)成完整 Demo (為了簡(jiǎn)化例子結(jié)構(gòu),這里只使用 addEventListener 監(jiān)聽(tīng)事件,請(qǐng)使用 Chrome, Firefox 等現(xiàn)代瀏覽器瀏覽 Demo )。
完整 Demo 。
本文由 Kayo Lee 發(fā)表,本文鏈接:http://kayosite.com/web-app-by-jquery-mobile-and-html5-offline-web-applications.html
聯(lián)系客服