最近做项目的时候,需要将web页面缓存到本地,经过调查,决定使用HTML5的应用程序缓存功能。
使用HTML5,通过创建 manifest 文件,可以轻松地创建 web 应用的离线版本。
请注意,根据MDN文档,该特性已经从 Web 标准中删除。推荐使用Service Workers代替,但是目前Service Workers 仍然有兼容性问题,所以应用程序缓存(ApplicationCache)仍然是目前进行离线存储的最好的方式。
HTML5 提供一种应用程序缓存机制,使得基于web的应用程序可以离线运行。开发者可以使用 Application Cache (AppCache) 接口设定浏览器应该缓存的资源并使得离线用户可用。 在处于离线状态时,即使用户点击刷新按钮,应用也能正常加载与工作。
index.html
...
cache.appcache
CACHE MANIFEST
#v1.0.18
/xxx.js
/js/jquery.min.js
/xxxx.js
/xxxxx.js
NETWORK:
*
FALLBACK:
404.html
注:
1.缓存清单文件可以使用任意扩展名,但传输它的 MIME 类型必须为 text/cache-manifest。
2.manifest 文件的建议的文件扩展名是:".appcache".
3.在缓存清单文件中列出的所有记录必须拥有相同的协议、主机名与端口号。
4.不要在清单文件中指定清单文件本身,否则将无法让浏览器得知清单文件有新版本出现。
manifest 文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容)。
manifest 文件可分为三个部分:
CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存。
NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存。可以使用通配符。
FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面)
注:
1.缓存清单文件的第一行必须包含字符串 CACHE MANIFEST,本行的其他文本会被忽略。
2.注释只能在所在行起作用,不能追加到其他行上。
3.CACHE, NETWORK, 和 FALLBACK 段落可以以任意顺序出现在缓存清单文件中,并且每个段落可以在同一清单文件中出现多次。而且段落允许为空。
4.段落标题指定了缓存文件即将操作的段落。有三个可选的标题:
段落标题 | 解释 |
---|---|
CACHE: | 切换到缓存清单的显式段落(默认段落)。 |
NETWORK: | 切换到缓存清单的在线白名单段落。 |
FALLBACK: | 切换到缓存清单的后备资源段落。 |
段落标题所在的行可以包含空白字符,段落名后的冒号 (? 不可省略。
5.1在默认 (CACHE:) 段落,每行都是一个合法的 URI 或 IRI ,与一个要缓存的资源相关联(本段落内不允许通配符)。
5.2在 Network 段落内,每行都是一个合法的 URI 或 IRI,关联一个需要通过网络获取的资源(本段落内可以使用通配符 *)。
5.3在 Fallback 段落内,每行都是一个合法的 URI 或 IRI(与一个资源关联),紧跟着一个后备资源,用于当无法与服务器建立连接时访问。
5.4相对 URI 是指相对于缓存清单的 URI,而不是包含清单的文档的 URI。
application cache的使用会修改文档的加载过程:
1.如果应用缓存存在,浏览器直接从缓存中加载文档与相关资源,不会访问网络。这会提升文档加载速度。
2.浏览器检查清单文件列出的资源是否在服务器上被修改。
3.如果清单文件被更新了, 浏览器会下载新的清单文件和相关的资源。 这都是在后台执行的,基本不会影响到webapp的性能。
下面详细描述了加载文档与更新应用缓存的流程:
1.当浏览器访问一个包含 manifest 特性的文档时,如果应用缓存不存在,浏览器会加载文档,然后获取所有在清单文件中列出的文件,生成应用缓存的第一个版本。
2.对该文档的后续访问会使浏览器直接从应用缓存(而不是服务器)中加载文档与其他在清单文件中列出的资源。此外,浏览器还会向 window.applicationCache 对象发送一个 checking 事件,在遵循合适的 HTTP 缓存规则前提下,获取清单文件。
3.如果当前缓存的清单副本是最新的,浏览器将向 applicationCache 对象发送一个 noupdate 事件,到此,更新过程结束。注意,如果你在服务器修改了任何缓存资源,同时也应该修改清单文件,这样浏览器才能知道它需要重新获取资源。
4.如果清单文件已经改变,文件中列出的所有文件—也包括通过调用 applicationCache.add() 方法添加到缓存中的那些文件—会被获取并放到一个临时缓存中,遵循适当的 HTTP 缓存规则。对于每个加入到临时缓存中的文件,浏览器会向 applicationCache 对象发送一个 progress 事件。如果出现任何错误,浏览器会发送一个 error 事件,并暂停更新。
5.一旦所有文件都获取成功,它们会自动移送到真正的离线缓存中,并向 applicationCache 对象发送一个 cached 事件。鉴于文档早已经被从缓存加载到浏览器中,所以更新后的文档不会重新渲染,直到页面重新加载(可以手动或通过程序).
一个特殊的值,用于表明一个应用缓存对象还没有完全初始化。
应用缓存此时未处于更新过程中。
清单已经获取完毕并检查更新。
下载资源并准备加入到缓存中,这是由于清单变化引起的。
一个新版本的应用缓存可以使用。有一个对应的事件updateready,当下载完毕一个更新,并且还未使用 swapCache() 方法激活更新时,该事件触发,而不会是 cached 事件。
应用缓存现在被废弃。
一旦应用被缓存,它就会保持缓存直到发生下列情况:
用户清空浏览器缓存
manifest 文件被修改(包括更改注释)
由程序来更新应用缓存
已知的applicationCache中的事件如下所示:
事件名称 | 解释 |
---|---|
oncached | 当离线资源存储完成后触发 |
onchecking | 当浏览器对离线存储资源进行检查的时候触发 |
ondownloading | 当浏览器开始下载离线资源的时候触发 |
onerror | 当缓存资源失败的时候触发 |
onnoupdate | 浏览器检查manifest文件没有更新时触发 |
onobsolete | 过时的 |
onprogress | 当浏览器缓存每一个资源的时候都会触发一次 |
onupdateready | 浏览器更新离线资源完成的时候触发 |
实例如下所示
// 离线资源存储完成之后触发
window.applicationCache.addEventListener('cached', function () {
console.log('cached');
});
// 离线存储资源进行更新检查的时候会触发
window.applicationCache.addEventListener('checking', function () {
console.log('checking');
});
// 开始下载离线资源的时候会触发
window.applicationCache.addEventListener('downloading', function () {
console.log('downloading');
});
// 下载每一个资源的时候会触发
window.applicationCache.addEventListener('progress', function () {
console.log('progress');
});
// 离线资源更新完成之后
window.applicationCache.addEventListener('updateready', function () {
console.log('updateready');
});
// 检查更新之后发现没有资源更新的时候触发
window.applicationCache.addEventListener('noupdate', function () {
console.log('noupdate');
});
// obsolete
window.applicationCache.addEventListener('obsolete', function () {
console.log('obsolete');
});
// error
window.applicationCache.addEventListener('error', function () {
console.log('error');
});
第一次加载后log:
缓存后再次打开log:
更新manifest文件后log:
将manifest文件中第五个文件设置为不存在的文件后的log:
断网后的log(chrome下):
上图是chrome浏览器模拟断网时的log,在手机浏览器真实断网的情况下触发以下两个事件:
1.onchecking
2.onerror
注:
1.onchecking事件每次刷新页面都会被触发,包括第一次加载。
2.onnoupdate事件触发的时机应该为manifest文件被更新之后,而不是缓存的资源被更新之后。
3.应用缓存可以变成废弃的。如果从服务器上移除一个应用的清单文件,浏览器将会清除所有清单中列出的应用缓存,并向 applicationCache 对象发送一个「obsolete」事件。这将使得应用缓存的状态变为 OBSOLETE。
当manifest文件更新之后,浏览器下载完成之后会触发onupdateready事件,但是页面并没有被刷新,即新的文件并没有执行,需要程序控制刷新一下。
window.applicationCache.addEventListener('updateready', function () {
if(window.applicationCache.status === window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
window.location.reload();
}
});
注:
本文主要参考了MDN上的文章,谨在此对开源文档开发编写人员表示敬意。