簡介
對於以網頁為基礎的應用程式而言,離線存取功能的重要性日益提升。是的,如果系統要求,所有瀏覽器都能將網頁和資源快取一段長時間,但瀏覽器可隨時將個別項目從快取中移除,騰出空間給其他項目。HTML5 會透過 ApplicationCache 介面解決部分離線問題。使用快取介面可為應用程式帶來三項優勢:
- 離線瀏覽:使用者可以在離線時瀏覽整個網站
- 速度:資源直接從磁碟取得,不必透過網路。
- 韌性:如果網站因「維護」而中斷 (例如有人不小心弄丟一切),使用者將可使用離線功能
開發人員可使用應用程式快取 (或 AppCache) 指定瀏覽器應快取哪些檔案,以供離線使用者使用。即使使用者在離線時按下重新整理按鈕,應用程式仍會載入並正常運作。
快取資訊清單檔案
快取資訊清單檔案是簡單的文字檔案,列出瀏覽器應快取哪些資源以供離線存取。
參照資訊清單檔案
如要為應用程式啟用應用程式快取,請在文件的 html
標記中加入資訊清單屬性:
<html manifest="example.appcache">
...
</html>
manifest
屬性應包含在您要快取的網頁應用程式的每個頁面上。如果網頁不含 manifest
屬性,瀏覽器就不會快取該網頁 (除非該屬性已在資訊清單檔案中明確列出)。也就是說,使用者瀏覽的任何網頁只要包含 manifest
,就會隱含地加入應用程式快取。因此,您不需要在資訊清單中列出每個網頁。如果網頁指向資訊清單,就無法避免該網頁遭到快取。
如要查看應用程式快取所控管的網址,請在 Chrome 中前往 about://://appcache-internals/。您可以在這裡清除快取並查看項目。Firefox 也有類似的開發人員工具。
manifest
屬性可以指向絕對網址或相對路徑,但絕對網址必須與網頁應用程式位於相同的來源。資訊清單檔案可以有任何副檔名,但必須使用正確的 mime 類型提供 (請參閱下方說明)。
<html manifest="http://www.example.com/example.mf">
...
</html>
資訊清單檔案必須使用 mime-type text/cache-manifest
提供。您可能需要在網路伺服器或 .htaccess
設定中新增自訂檔案類型。
舉例來說,如要在 Apache 中提供此 mime-type,請在設定檔中加入這一行:
AddType text/cache-manifest .appcache
或者,您也可以在 Google App Engine 的 app.yaml 檔案中執行以下操作:
- url: /mystaticdir/(.*\.appcache)
static_files: mystaticdir/\1
mime_type: text/cache-manifest
upload: mystaticdir/(.*\.appcache)
這項規定已在一段時間前從規格中移除,最新版本的 Chrome、Safari 和 Firefox 也已不再要求這項規定,但你必須使用 mime-type,才能在舊版瀏覽器和 IE11 中運作。
資訊清單檔案的結構
資訊清單是您透過 HTML 元素的資訊清單屬性連結到的獨立檔案。簡單的資訊清單如下所示:
CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js
這個範例會在指定這個資訊清單檔案的網頁中,快取四個檔案。
請注意以下幾點:
CACHE MANIFEST
字串是第一行,也是必填項目。- 檔案可以來自其他網域
- 部分瀏覽器會限制應用程式可使用的儲存空間配額。舉例來說,在 Chrome 中,AppCache 會使用其他離線 API 可共用的 共用資源池,用於暫時性儲存空間。如果您是為 Chrome 線上應用程式商店編寫應用程式,使用
unlimitedStorage
即可移除這項限制。 - 如果資訊清單本身傳回 404 或 410,系統就會刪除快取。
- 如果清單檔案或其中指定的資源無法下載,整個快取更新程序都會失敗。在發生失敗的情況下,瀏覽器會繼續使用舊版應用程式快取。
我們來看看更複雜的範例:
CACHE MANIFEST
# 2010-06-18:v2
# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
stylesheet.css
images/logo.png
scripts/main.js
# Resources that require the user to be online.
NETWORK:
*
# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
# offline.html will be served in place of all other .html files
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
以「#」開頭的行是註解行,但也可以用於其他用途。應用程式的快取只會在其資訊清單檔案變更時更新。舉例來說,如果您編輯圖片資源或變更 JavaScript 函式,系統就不會重新快取這些變更。您必須修改資訊清單檔案本身,才能通知瀏覽器重新整理快取檔案。
請避免使用持續更新的時間戳記或隨機字串,以免每次都強制更新。系統會在更新期間檢查資訊清單兩次,一次開始時、更新所有快取檔案後一次。如果資訊清單在更新期間發生變更,瀏覽器可能會從一個版本擷取部分檔案,從另一個版本擷取其他檔案,因此不會套用快取,並在稍後重試。
雖然快取會更新檔案,但瀏覽器不會使用這些檔案,直到網頁重新整理為止,因為網頁是從目前的快取版本載入之後。
資訊清單可包含三個不同的部分:CACHE
、NETWORK
和 FALLBACK
。
CACHE:
- 這是項目的預設版面。在這個標頭下方 (或
CACHE MANIFEST
後方) 列出的檔案,會在首次下載後明確快取。NETWORK:
- 如果檔案不在快取中,則這裡列出的檔案可能來自網路。如果檔案不在快取中,則這裡列出的檔案可能來自網路。如果檔案不在快取中,則這裡列出的檔案可能來自網路。您可以在此處將特定網址加入白名單,也可以直接輸入「」,允許所有網址。大部分網站都需要「 」。
- 這是選用部分,可指定在無法存取資源時的備用網頁。第一個 URI 是資源,第二個 URI 是網路要求失敗或發生錯誤時使用的備用項。兩個 URI 必須與資訊清單檔案來自相同來源。您可以擷取特定網址,也可以擷取網址前置字元。「images/large/」會擷取來自網址的失敗情形,例如「images/large/whatever/img.jpg」。
FALLBACK:
下列資訊清單定義了「萬用」頁面 (offline.html),當使用者在離線時嘗試存取網站根目錄時,系統會顯示這個頁面。它也宣告所有其他資源 (例如位於遠端網站上的資源) 都需要網際網路連線。
CACHE MANIFEST
# 2010-06-18:v3
# Explicitly cached entries
index.html
css/style.css
# offline.html will be displayed if the user is offline
FALLBACK:
/ /offline.html
# All other resources (e.g. sites) require the user to be online.
NETWORK:
*
# Additional resources to cache
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png
更新快取
應用程式離線後,系統會持續快取應用程式,直到發生下列任一情況為止:
- 使用者清除瀏覽器儲存的網站資料。
- 資訊清單檔案已修改。注意:更新資訊清單中列出的檔案,並不代表瀏覽器會重新快取該資源。必須修改資訊清單檔案本身。
快取狀態
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;
};
如要透過程式輔助方式檢查資訊清單更新,請先呼叫 applicationCache.update()
。這會嘗試更新使用者的快取 (需要變更資訊清單檔案)。最後,當 applicationCache.status
處於 UPDATEREADY
狀態時,呼叫 applicationCache.swapCache()
會更換新快取的舊快取。
var appCache = window.applicationCache;
appCache.update(); // Attempt to update the user's cache.
...
if (appCache.status == window.applicationCache.UPDATEREADY) {
appCache.swapCache(); // The fetch was successful, swap in the new cache.
}
好消息是,您可以自動執行這項操作。如要將使用者更新至網站的最新版本,請設定監聽器,以便在網頁載入時監控 updateready
事件:
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
} else {
// Manifest didn't changed. Nothing new to server.
}
}, false);
}, false);
AppCache 事件
如您所料,我們會提供其他事件,以便監控快取的狀態。瀏覽器會觸發事件,例如下載進度、更新應用程式快取和錯誤情況。下列程式碼片段會為每種類型的快取事件設定事件監聽器:
function handleCacheEvent(e) {
//...
}
function handleCacheError(e) {
alert('Error: Cache failed to update!');
};
// Fired after the first cache of the manifest.
appCache.addEventListener('cached', handleCacheEvent, false);
// Checking for an update. Always the first event fired in the sequence.
appCache.addEventListener('checking', handleCacheEvent, false);
// An update was found. The browser is fetching resources.
appCache.addEventListener('downloading', handleCacheEvent, false);
// The manifest returns 404 or 410, the download failed,
// or the manifest changed while the download was in progress.
appCache.addEventListener('error', handleCacheError, false);
// Fired after the first download of the manifest.
appCache.addEventListener('noupdate', handleCacheEvent, false);
// Fired if the manifest file returns a 404 or 410.
// This results in the application cache being deleted.
appCache.addEventListener('obsolete', handleCacheEvent, false);
// Fired for each resource listed in the manifest as it is being fetched.
appCache.addEventListener('progress', handleCacheEvent, false);
// Fired when the manifest resources have been newly redownloaded.
appCache.addEventListener('updateready', handleCacheEvent, false);
如果資訊清單檔案或其中指定的資源無法下載,整個更新就會失敗。在這種情況下,瀏覽器會繼續使用舊版應用程式快取。
參考資料
- ApplicationCache API 規格
- Application Cache 是個騙子 - 說明 AppCache 的陷阱和問題。