问题描述:
在开发h5的webapp时,会需要考虑app的页面加载性能、流量节省、离线缓存等问题。解决这类问题的一种方法可以考虑appcache技术,它可以将一些静态资源缓存到客户端本地,这样同一个资源不用反复的从服务器请求并下载到本地了,既可以提高页面的加载性能,也能节省一些流量。
如何使用appcache技术?
appcache缓存技术主要通过配置文件实现。自定义一个appcache结尾的配置文件,配置需要缓存的资源,然后将这个配置文件引入到需要使用缓存的前台页面就行了。
1.自定义配置文件
定义一个配置文件,文件名称自己随便命名,但是结尾格式为appcache。
这个就是缓存配置文件。文件配置信息如下:
CACHE MANIFEST
#v2.0.0(更新客户端缓存文件的话,修改版本号就行了)
#需要缓存的文件列表;系统级别的,不经常变的文件可以放在这里;
CACHE:
../../../static/bootstrap/css/bootstrap.min.css
../../../static/bootstrap/css/bootstrap-theme.min.css
../../../static/jquery/jquery.min.js
../../../static/css/model.css
../../../static/css/style.css
../../../static/HBuilder/css/mui.min.css
../../../static/HBuilder/js/mui.min.js
../../../static/js/comm.js
../../../static/android/android-require-min-v2.1.11.js
../../../static/android/android-v0.0.1.js
../../../static/js/ajaxSetup.js
../../../static/img/index_bg.png
../../../static/HBuilder/fonts/mui.ttf
#不需要缓存的文件列表;与模块相关的动态脚本文件等放在这里;
NETWORK:
*
#代替方案;找不到的资源可以用其他的资源代替
FALLBACK:
注意:
文件第一行必须是CACHE MANIFEST关键字,些其他的信息的话,缓存会不好用;
相对路径是相对这个缓存配置文件而言的,并不是相对前台页面;
2.引用配置文件
将配置文件通过manifest属性引入到前台页面的html元素里:
<!DOCTYPE html>
<html manifest="../zsfy/login/cache/beforeLogin.appcache">
<head>
<meta charset="utf-8" />
<title>登录引导页</title>
浏览器访问这个页面,用f12查看调试页面的resources选项,缓存信息如下:
缓存成功的话,控制台信息如下:
可以看到总共缓存了14个文件。
当再次访问这个页面时,浏览器就会直接从缓存里拿资源文件了:
缓存加载过程如图:
注意:
(1) manifest的加载是晚于页面其他资源的。浏览器先加载引用的资源(从服务器或缓存中拿),再通过检查 manifest 文件是否有更新来更新缓存文件。这样缓存文件可能用的不是最新的版本。这个是比较坑的。。。
(2)不同的html页面,配置不同的manifest文件,他们的缓存空间是单独分开的,缓存的资源之间相互独立。验证这个结论的方法:
★ 定义一个manifest文件,配置缓存资源test.js文件;
★ a.html页面和b.html页面配置manifest文件;
★ 浏览器访问a.html页面,用F12查看test.js里面的内容;
★ 更改服务器里的test.js文件内容,浏览器访问b.html页面,用F12查看test.js里面的内容为更改后的内容;
★ 浏览器访问a.html页面,用F12查看test.js里面的内容;反复刷新页面,发现还是更改之前的内容;
可见,不同页面的缓存空间是独立的。但是整个网站的缓存上限通常是5M.
(3)不同的html页面,配置同一个manifest文件,他们的缓存空间是共享的。比如a,b两个页面引用同一份manifest A. 则更新A,更新R,刷新b, b对应的R资源更新后,a的R资源副本也会随之更新. 这就是cache group的机制。a和b对应的application cache,同属于同一个application cache group
(4)如果manifest文件或者是其中指定的某个资源下载失败的话,整个cache的更新都会失败。在这种情况下,浏览器将会使用老的application cache。
如何更新缓存?
有缓存必然涉及到缓存的更新,更新缓存的方法如下:
(1)更改manifest文件
每当manifest文件有更改,哪怕是加了一个空格,浏览器会重新下载资源文件进行缓存。通常做法是通过修改注释信息里的版本号来实现文件的更改。
(2)手动清除浏览器历史记录
如用用户手动清了浏览器缓存,下次加载时,浏览器会重新生成缓存,也可算是一种缓存的更新。
(3)js脚本更新缓存
在程序中,你可以通过window.applicationCache 对象来访问浏览器的app cache。你可以查看 status 属性来获取cache的当前状态:
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;
};
为了通过编程更新cache,首先调用 applicationCache.update()。这将会试图更新用户的 cache(要求manifest文件已经改变)。最后,当 applicationCache.status 处于 UPDATEREADY 状态时, 调用applicationCache.swapCache(),旧的cache就会被置换成新的。
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.
}
像这样使用 update()和swapCache()并不会将更新后的资源 呈现给用户。这仅仅是让浏览器检查manifest文件是否发生了更新,然后下载指定的更新内容,重新填充app cache。因此,要让用户看到更新后的内容,需要两次页面下载,一次是更新app 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.
// Swap it in and reload the page to get the new hotness.
window.applicationCache.swapCache();
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知识点总结
(1)备用资源FALLBACK列表里的资源,必须与当前的manifest同源;备用资源也会被缓存.
(2)备用名称空间 和 白名单名称空间 都可以使用前缀匹配模式。即支持通配符匹配模式。可以放心的是 //www.a.com/abc 是不匹配 //www.a.com/ab的,因为//www.a.com/ab 实际上是//www.a.com/ab/
(3)前缀匹配对端口的匹配是宽松的.如abc.com:80/a.png 就会被 abc.com/所匹配.
(4)window. applicationCache.update(), 只会立刻检测manifest文件,而不会更新相应资源;
(5)一组不同的页面引入同一个manifest文件时,这组页面构成一个group.并已document作为标识,来区分他们。其中任何一个的manifest或资源更新,甚至是检测都会触发其他页面的applicationCache的相应事件.
(6)a,b两个页面引入相同资源,但a有使用manifest而b没有。那么即使a页面缓存了资源,这个缓存对b页面也不会有效。
(7)为manifest文件配置304相关头域时,也配置expires和cache-control : max-age。因为chrome,safari,以及android,如果没有expires 或 max-age时,是不会有304的,而只会是200。opera则无视一切http cache头域.总是200。