简介
离线访问对基于网络的应用而言越来越重要。是的,所有浏览器都可以如果被告知长时间缓存网页和资源,但是浏览器可能会随时将个别项目从缓存中逐出,为其他内容腾出空间。HTML5 使用 ApplicationCache 接口解决了离线的一些问题。 使用缓存接口可为您的应用带来三个好处:
- 离线浏览 - 用户可以在离线状态下浏览您的整个网站
- 速度 - 资源直接来自磁盘,无需传输到网络。
- 弹性 - 如果您的网站因“维护”而出现故障(例如,有人不小心破坏一切),用户将获得离线体验
应用缓存(即 AppCache)允许开发者指定浏览器应缓存哪些文件以供离线用户使用。即使用户在离线状态下按下刷新按钮,您的应用也将正常加载和运行。
缓存清单文件
缓存清单文件是一个简单的文本文件,其中列出了浏览器应缓存以供离线访问的资源。
引用清单文件
如需为应用启用应用缓存,请在文档的 html
标记中添加清单属性:
<html manifest="example.appcache">
...
</html>
在要缓存的 Web 应用的每个页面上,都应添加 manifest
属性。如果页面不包含 manifest
属性,浏览器不会缓存该页面(除非清单文件本身明确列出该属性)。这意味着用户导航到的包含 manifest
的任何页面都会隐式添加到应用缓存中。因此,您无需在清单中列出每个网页。如果网页指向清单,则无法阻止该网页被缓存。
您可以在 Chrome 中访问 about://appcache-internals/,查看由应用缓存控制的网址。在这里,您可以清除缓存并查看条目。Firefox 中有类似的开发者工具。
manifest
属性可以指向绝对网址或相对路径,但绝对网址必须与 Web 应用同源。清单文件可以使用任何文件扩展名,但需要使用正确的 MIME 类型提供(见下文)。
<html manifest="http://www.example.com/example.mf">
...
</html>
清单文件必须以 text/cache-manifest
类型提供。您可能需要在网络服务器或 .htaccess
配置中添加自定义文件类型。
例如,要在 Apache 中提供此 MIME 类型,请将下面这行代码添加到您的配置文件中:
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 就不再需要此要求。但是,在旧版浏览器和 IE11 中必须使用 MIME 类型。
清单文件的结构
该清单是您通过 html 元素上的 manifest 属性链接到的独立文件。一个简单的清单如下所示:
CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js
本例将在指定此清单文件的网页上缓存四个文件。
您需要注意以下几点:
CACHE MANIFEST
字符串是第一行,必需提供。- 文件可以来自其他网域
- 某些浏览器会限制您的应用可用的存储空间配额。例如,在 Chrome 中,AppCache 使用其他离线 API 可以共享的 TEMPORARY 存储空间共享池。如果您要为 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:
- 如果此部分中列出的文件不在缓存中,它们可能来自网络;否则,即使用户已联网,系统也不会使用相应网络。您可以在此将特定网址列入白名单,也可以直接使用“”(这样会允许所有网址)。大多数网站都需要使用“”。
FALLBACK:
- 此部分是可选的,用于指定无法访问资源时的后备网页。第一个 URI 是资源,第二个是在网络请求失败或出错时使用的回退机制。两个 URI 必须与清单文件来自同一来源。您可以捕获特定网址,也可以捕获网址前缀。“images/large/”会捕获“images/large/whatever/img.jpg”等网址中的失败情况。
以下清单定义了一个“综合”网页 (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 规范
- 应用缓存是一个垃圾桶 - 涵盖 AppCache 的陷阱和问题。