HTML5提供了一种离线应用缓存机制,使得网页应用可以离线使用,这种机制在移动端浏览器上支持度非常广,所有版本的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上触发一系列事件。
事件名称 |
解释 |
后续事件 |
checking |
checking事件是整个事件序列中的第一个,表示浏览器在首次下载缓存,或者在检查缓存是否有更新 |
noupdate, downloading, obsolete, error |
noupdate |
noupdate事件是整个事件序列的最后一个,用来表示缓存没有更新 |
无 |
downloading |
浏览器正在首次下载menifest中列出的离线资源,或者浏览器发现缓存有更新,并且正在下载该更新 |
progress, error, cached, updateready |
progress |
浏览器正在下载menifest中的离线资源,其中progressEvent对象中total表示需要下载的总资源的数量,loaded表示已经处理过的资源数量 |
progress, error, cached, updateready |
cached |
浏览器已经成功下载了menifest中的离线资源,应用已经被缓存 |
无 |
updateready |
缓存有更新并且更新已经下载完成,这是可以使用ApplicationCache.swapCache()来将应用切换到最新缓存 |
无 |
obsolete |
应用已经有离线缓存了,但menifest文件无法加载成功,则删除现有离线缓存 |
无 |
error |
发生错误的情况比较多:可能是首次视图缓存离线文件时,发现menifest文件返回404;也可能是下载缓存过程中发生了fatal错误,比如超出了最大可用存储空间的限制,发现了header为Cache-Control: no-store的资源;还有可能是缓存在更新过程中发现menifest文件发生了变化 |
无 |
关于离线缓存中要触发的事件,有一个很有趣的特性。从离线缓存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("路径"); //缓存路径
参考资料
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Using_the_application_cache
https://www.w3.org/TR/html5/