建立運行在手機上的web app時,鑒於手機使用者的網路情況,我們需要考慮到使用者離線使用的情況。
html5支援構建離線應用程式。使用它的本機快取機制可以將應用所需的資源檔都緩衝到本地,從而實現應用的離線使用。首先要說明的是,本機快取和傳動的瀏覽器網頁緩衝是不同的,網頁緩衝基於網頁,也就是緩衝一個網頁的內容,而不是整個app。同時網頁緩衝並不可靠,我們不知道我們的app中哪個頁面已經緩衝,該頁面的哪些資源已經緩衝,而本機快取對於緩衝內容是完全可控的。
使用離線緩衝,除了可以使應用可以離線使用外,還能協助有效加快網頁載入速度(本地的自然更快),同時降低伺服器負載(只需要下載更新的內容)。
正如之前所提到的,本機快取可以指定要緩衝的內容,這同過配置manifest來實現。可以為整個app配置manifest,也可以為單獨某個頁面來配置。
簡單的manifest格式如下:
CACHE MANIFESTindex.htmlstylesheet.cssimages/logo.pngscripts/main.js
檔案的第一行必須是CACHE MANIFEST。
該manifest聲明了需要緩衝的html頁面,css,圖片以及js檔案。
再看一個比較複雜的manifest檔案:
CACHE MANIFEST# 指定一個版本號碼# version 1# 該類別指定要緩衝的資源檔CACHE:/favicon.icoindex.htmlstylesheet.cssimages/logo.pngscripts/main.js# 指定不進行緩衝的資源檔NETWORK:login.phphttp://foocoder.com# 每行指定兩個檔案,第一個為線上時使用的資源,第二個是離線時使用的資源FALLBACK:/main.py /static.htmlimages/large/ images/offline.jpg*.html /offline.html
其中#號開頭的為注釋。因為只有在manifest檔案發生改變時才會更新,所以我們可以加個版本號碼方便控制。
從該檔案中可以看到分為了三個類別:
CACHE類別指定需要被緩衝的資源檔。
NETWORK類別指定不緩衝的資源檔,即只在連網的情況下才能訪問。
FALLBACK每一行都會指定兩個檔案,第一個為線上時使用的資源,第二個為離線時使用的備用資源。其中*為萬用字元,表示線上時使用所有的.html檔案。
配置好manifest檔案之後,我們只需要在頁面上引用即可。如下,在html 標籤的manifest屬性下指定manifest檔案的地址:
<html manifest="app.manifest">...</html>
該地址可以是絕對位址也可以是相對位址,但是該檔案的嗎MIME 類型必須是text/cache-manifest,所以需要在伺服器做相應配置對該類型添加支援,例如嗎,對於apache伺服器,需要在配置mime.types中添加如下內容:
AddType text/cache-manifest .manifest
到這裡為止,就完成了離線緩衝的基本內容,在manifest檔案發生變化時,瀏覽器會檢查manifest檔案並更新緩衝。
我們不得不考慮一個問題,瀏覽器如何處理本機快取?當服務端更新了應用程式後,使用者開啟時是不是會使用最新的資源了?答案是否定的。這需要瞭解下在使用離線緩衝的情況下,瀏覽器與服務端的整個互動過程。
1.首次訪問
在首次訪問時,沒有什麼特別,瀏覽器解析index.html,請求所有的資源檔。隨後就會處理manifest檔案,請求所有的manifest中的資源檔,注意,即使之前已經請求過了所有的資源檔,這裡必須進行重複請求。最後將這些檔案快取到本地。
2.再次訪問
再次訪問時,瀏覽器發現有本機快取,所以會載入本機快取內容。隨後會向服務端請求manifest檔案,如果manifest檔案未更新,返回304代碼,瀏覽器不做處理。如果manifest已經更新過,則請求所有manifest中的資源檔,重新對其緩衝。
所以,即使服務端更新了manifest和其他資源,使用者開啟時扔是之前的頁面。需要重新開啟才能使用更新過後的資源。
有辦法立刻更新緩衝嗎?是可以的。我們可以使用applicationCache對象做到這一點。但是也只是能做到立刻更新緩衝,還是需要使用者重新開啟也沒才會生效。接下來就看看如何用applicationCache對象立刻更新緩衝。
window.applicationCache下有個status屬性。可以通過其知道當前的緩衝狀態
var appCache = window.applicationCache;switch (appCache.status) { case appCache.UNCACHED: // UNCACHED == 0 return 'UNCACHED'; break; case appCache.IDLE: // IDLE == 1 return 'IDLE'; break; case appCache.CHECKING: // CHECKING == 2 return 'CHECKING'; break; case appCache.DOWNLOADING: // DOWNLOADING == 3 return 'DOWNLOADING'; break; case appCache.UPDATEREADY: // UPDATEREADY == 4 return 'UPDATEREADY'; break; case appCache.OBSOLETE: // OBSOLETE == 5 return 'OBSOLETE'; break; default: return 'UKNOWN CACHE STATUS'; break;};
既然可以獲得狀態,我們只需要請求更新,隨後在狀態為appCache.UPDATEREADY時更新緩衝時即可。
applicationCache.update()方法會嘗試更新使用者緩衝,而applicationCache.swapCache()方法會對本機快取進行更新:
var appCache = window.applicationCache; appCache.update(); // 開始更新 if (appCache.status == window.applicationCache.UPDATEREADY) { appCache.swapCache(); // 更新緩衝}
正如之前所說的,即使更新了緩衝,還是需要重新載入才能使用最新的資源,此時可以提示使用者更新。只需要監聽onUpdateReady事件,該事件在緩衝被下載到本地後出發,從而可以在此時提示使用者:
window.addEventListener('load', function(e) { window.applicationCache.addEventListener('updateready', function(e) { if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { //更新本機快取 window.applicationCache.swapCache(); if (confirm('已經有新的版本,是否立刻切換到最新版?')) { window.location.reload(); } } else { } }, false);}, false);
applicationCache對象還提供了其他事件,分別為:
onchecking,onerror,onnoupdate,ondownloading,onprogress,onupdateready,oncached和onobsolete
在整個瀏覽器與服務端互動的過程中,所有的錯誤都會出發error事件,我們可以通過監聽error事件進行處理:
var appCache = window.applicationCache; appCache.addEventListener('error', handleCacheError, false); function handleCacheError(e) { alert('Error: Cache failed to update!');};
歡迎留言交流。