深入理解HTML5離線快取機制

積木村の研究所發表於2016-04-12

TML5提供了一種離線應用快取機制,使得網頁應用可以離線使用,這種機制在移動端瀏覽器上支援度非常廣,所有版本的android和ios瀏覽器都能很好的支援。我們可以放心的使用該特性來加速移動端頁面的訪問速度。

開啟離線快取的步驟也非常簡單:

(1) 準備快取清單檔案(menifest text/cache-manifest),用於描述頁面需要快取的資源列表
(2) 在需要離線使用的頁面中新增menifest屬性,用於指定快取清單檔案的路徑

讓我們首先理解瀏覽器實現離線快取的詳細步驟,然後探討使用離線快取加速移動端網頁訪問速度的方案。

1. 下載/更新快取的詳細步驟

(1)當瀏覽器訪問一個包含 manifest 特性的文件時,如果應用快取不存在,瀏覽器會載入文件,然後獲取所有在清單檔案中列出的檔案,生成應用快取的第一個版本。

(2)對該文件的後續訪問會使瀏覽器直接從應用快取(而不是伺服器)中載入文件與其他在清單檔案中列出的資源。此外,瀏覽器還會向 window.applicationCache 物件傳送一個 checking 事件,在遵循合適的 HTTP 快取規則前提下,獲取清單檔案。

(3)如果當前快取的清單副本是最新的,瀏覽器將向 applicationCache 物件傳送一個 noupdate 事件,到此,更新過程結束。注意,如果你在伺服器修改了任何快取資源,同時也應該修改清單檔案,這樣瀏覽器才能知道它需要重新獲取資源。

(4)如果清單檔案已經改變,檔案中列出的所有檔案—也包括通過呼叫 applicationCache.add() 方法新增到快取中的那些檔案—會被獲取並放到一個臨時快取中,遵循適當的 HTTP 快取規則。對於每個加入到臨時快取中的檔案,瀏覽器會向 applicationCache 物件傳送一個 progress 事件。如果出現任何錯誤,瀏覽器會傳送一個 error 事件,並暫停更新。

(5)一旦所有檔案都獲取成功,它們會自動移送到真正的離線快取中,並向 applicationCache 物件傳送一個 cached 事件。鑑於文件早已經被從快取載入到瀏覽器中,所以更新後的文件不會重新渲染,直到頁面重新載入(可以手動或通過程式).

以上只是一些詳細步驟,具體也有一些值得注意的細節,比如menifest檔案中列出的資源url必須和menifest本身使用同樣的網路協議,如果menifest檔案使用的是http協議,則列表中https協議的檔案就會被忽略。總之,每當在使用Application Cache的過程中遇到奇怪的問題時,隨時查閱W3C標準文件

2.離線快取事件流

瀏覽器在解析HTML文件的過程中,遇到HTML標記的menifest屬性時,就立即在後臺開啟一個新的程式下載需要離線快取的資源。在下載離線快取的過程中,會在ApplicationCache上觸發一系列事件。

離線快取事件流

關於離線快取中要觸發的事件,有一個很有趣的特性。從離線快取W3C標準中,我們經常看到如下描述:'queue a post-load task to fire a simple event named (checking|noupdate|downloading)',每一個HTML文件都有一個離線快取事件佇列(queue),離線快取下載過程中的事件都存放在這個佇列裡,用於在文件的onload事件觸發後執行。也就是說,所有的ApplicationCache事件都在html的onload事件觸發後才觸發。

3.使用離線快取加速移動端網頁開發

上文提到過,Application Cache在移動端支援的很好,幾乎所有的android瀏覽器和ios瀏覽器都能很好的支援。事實上使用Application Cache加速移動端網頁訪問速度是行業類普遍採用的優化方案,在包括新浪微博和QQ瀏覽器等大型產品中都有非常廣泛的使用。但是在使用離線快取時,我們需要留意一些問題。

(1).二次更新的問題

我們知道每次使用離線應用時,在有網路連線的情況下,瀏覽器都會逐位元組的檢查menifest檔案是否有更新,而當menifest檔案有更新時,就會重新下載menifest檔案中列出的所有資源,資源下載成功後會觸發updateready事件。這時離線應用本身並不會立即更新,而會在下次訪問時才更新,這就是我們所說的二次更新。我們在開發web程式時,一般都是前端頁面和後端介面同步更新,但是二次更新問題會導致頁面更新不受控制,無法和後端介面同步更新,因此要做好後端介面的向前相容,這迫使我們拋棄傳統的web開發思路而採取native開發思路來管理離線應用。我們可以通過檢測updateready事件,在新的快取可用時通知使用者更新。

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();  
            }  
        }  
    }, false);  
}, false);  

(2).超出大小限制的問題

Application Cache的W3C標準中並沒有對大小限制做出詳細的描述。因此瀏覽器實現起來也是參差不齊,為了最大化的使用離線快取,我們應該清楚自己業務的目標瀏覽器的離線快取大小限制,使用chrome://appcache-internals/可以輕易的管理chrome瀏覽器的離線快取。我們可以寫一個DEMO,採用二分搜尋法,不斷的測試瀏覽器的Application Cache大小限制,直到觸發相應的error事件。這個連結可以測試Application Cache的大小限制。通過監聽Application的error事件來能夠處理超出離線快取大小限制的情況:

window.applicationCache.addEventListener('error',function(e){
    if(e.reason == 'quota'){
        //超出離線快取大小限制
    }
});

離線快取的是一套網頁加速方案,超出Application Cache的大小限制後,會對我們的應用有不同的影響,具體表現就是:

首次下載快取時超出大小,所有資源都不會快取,而是請求網路,應用功能正常。
更新資源後超出大小,快取不會更新,應用無法更新。

(3).webview中的問題

在標準的html5瀏覽器中,我們可以放心的使用Application Cache,並且不需要任何設定。但是在webview中,則可能需要顯示的設定,比如android系統的webview預設是不支援Application Cache的,因此需要顯示設定:

webView.getSettings().setAppCacheEnabled(true);        //預設是關閉的
webView.getSettings().setAppCacheMaxSize(1024*1024*5); //快取大小
webView.getSettings().setAppCachePath("路徑");          //快取路徑

本文同時發表在我的部落格積木村の研究所http://foio.github.io/html5-application-cache/

相關文章