HTML5离线存储归纳及使用实例
HTML5离线存储归纳
一、离线存储的作用
1、用户可离线访问你的应用,这对于无法随时保持联网状态的移动终端用户来说尤其重要
2、用户访问本地的缓存文件,通常意味着更快的访问速度
3、仅仅加载被修改过的资源,避免同一资源对服务器多次的请求,大大降低了对服务器的访问压力
二、如何实现离线存储
1、在html标签里通过manifest属性引用一个cache.manifest文件,该文件里声明了浏览器需缓存的所有资源文件,如下所示:
<!DOCTYPE html>
<html manifest='cache.manifest'>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>离线缓存示例页面</title>
<!-- CSS文件引用 -->
</head>
<body>
<!-- 各种标签 -->
</body>
<!-- 各种脚本文件 -->
</html>
2、关于cache.manifest的定义
CACHE MANIFEST
# 注释:需要缓存的文件,无论在线与否,均从缓存里读取
chched.js
cached.css
# 注释:不缓存的文件,无论缓存中存在与否,均从新获取
NETWORK:
uncached.js
uncached.css
# 注释:获取不到资源时的备选路径,如index.html访问失败,则返回404页面
FALLBACK:
index.html 404.html
3、几个需要关注的细节
cache.manifest 文件的MIME类型是text/cache-manifest,至于如何让web服务器返回.manifest文件时添加 Content-Type: text/cache-manifest,不同服务器配置细节不同,此处不展开
cache.manifest写法以CACHE MANIFEST开头,文件编码格式必须是utf-8
引用cache.manifest的html文档会被默认包含进缓存清单
4、归纳起来,步骤如下:
配置服务器支持 cache.manifest 的Content-type: manifest
编写 cache.manifest 文件
html页面引用cache.manifest文件
三、applicationCache对象,及属性、事件、接口
浏览器通过window.applicationCache对象来及其相应属性、接口、事件供用户构建离线应用,详细可见 applicationCache
//当前文档对应的applicationCache对象
window.applicationCache
//当前缓存所处的状态,为0~5的整数值,分别对应一个状态,并分别对应一个常量
window.applicationCache.status
window.applicationCache.UNCACHED === 0 //未缓存,比如一个页面没有制定缓存清单,其状态就是UNCACHED
window.applicationCache.IDLE === 1 //空闲,缓存清单指定的文件已经全部被页面缓存,此时状态就是IDLE
window.applicationCache.CHECKING === 2 //页面正在检查当前离线缓存是否需要更新
window.applicationCache.DOWNLOADING === 3 //页面正在下载需要更新的缓存文件
window.applicationCache.UPDATEREADY === 4 //页面缓存更新完毕
window.applicationCache.OBSOLETE === 5 //缓存过期,比如页面检查缓存是否过期时,发现服务器上的.manifest文件被删掉了
//常用API,在后面会稍详细介绍
window.applicationCache.update() //update方法调用时,页面会主动与服务器通信,检查页面当前的缓存是否为最新的,如不是,则下载更新后的资源
window.applicationCache.swapCache() //updateready后,更新到最新的应用缓存
除了上面提及的属性、接口外,window.applicationCache还包含了一系列的事件,大部分与上面提到的 window.applicationCache.status对应,如下:
事件名 关联属性 事件释义
onchecking CHECKING 见上文对关联属性的解释
ondownloading DOWNLOADING -
onupdateready UPDATEREADY -
onobsolete OBSOLETE -
oncached IDLE -
onerror 更新出错,如检查更新时.manifest被人误删了
onnoupdate 检查后,发现缓存无需更新
onprogress -
四、访问缓存应用,相应触发事件,及其对应状态
1、假设我们在第一次在chrome里面访问http://localhost/blog/html5/has_cache/cache.html,如下所示:
打开控制台,看到如下:
由上到下依次为(cache = window.applicationCache):
检查是否需要下载/更新缓存,cache.status===cache.CHECKING
发现本地还没有缓存文件,开始下载,cache.status===cache.DOWNLOADING
正在下载缓存文件,cache.staus===cache.PROGRESS
下载完毕,并缓存在本地,cache.status===cache.CACHED
2、再次访问http://localhost/blog/html5/cache/has_cache/cache.html
1)假设.manifest没有变化
打开控制台,如下所示:
由上到下依次为:
检查是否需要下载/更新缓存,cache.status===cache.CHECKING
发现服务器上没有更新,cache.status===cahce.NOUPDATE
2)假设cache.manifest已经发生变化,则如下截图所示,不赘述:
五、如何更新离线缓存
1、本地手动删除,各浏览器实现方式不同。以chrome为例,输入chrome://appcache-internals/,可以查看本地的离线缓存,也可以进行删除
2、更新.manifest文件,浏览器检测到.manifest变更后,会主动更新本地缓存。(需要注意的是,假如没有更新.manifest,即使你对缓存清单里的文件进行了修改,浏览器依旧会顽强地从本地缓存里面读取修改之前的文件)
3、通过applicationCache对象的API来主动更新,主要涉及接口为update()以及swapCache()
cache.update()
官方说明:启动应用缓存下载进程,由于浏览器通常会主动检查.manifest文件确认缓存是否需要更新,所以大部分情况下这个方法是没必要的。但对于一些可能在浏览器里待上长达一个星期左右的应用,比如电子邮箱,这个方法就会排上用场,比如每隔1天检查下本地缓存的一些文件在服务器上是否已经更新。
个人理解:
cache.swapCache()
官方说明:如果浏览器已经更新了新的离线缓存,则切到最新的缓存去。对于已经加载解析的资源,如CSS、图片等,并不会导致其重新加载、解析一遍。唯一的变化就是,后续对缓存资源的请求,获取到的都是本地的最新缓存。需要注意的是,swapCache方法需要在updateready事件触发后调用。
个人理解:调用update方法,更新了本地缓存,但如果不掉用swapCache方法,在本次会话中,重新请求已经更新过的资源,还是拿到老的文件。
六、demo演示:update后是否调用swapCache的区别
假设有cache.html、cache.manifest、cache.js,分别如下:
<!DOCTYPE html>
<html manifest='cache.manifest'>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>离线缓存</title>
</head>
<body></body>
<script type="text/javascript" src="cache.js"></script>
<script type="text/javascript">
var cache = window.applicationCache;
conso.log('test:' + test); //cache.js定义的一个变量,初始值为10
function load(url, callback){
var script = document.createElement('script');
script.src = url;
script.onload = function(){
callback && callback();
}
document.body.appendChild(script);
}
setTimeout(function(){
cache.addEventListener('updateready', function(){
log('更新完毕');
//cache.swapCache();
load('cache.js', function(){
log('test:' + test); //test: 10
});
});
cache.update();
}, 20*1000);
</script>
</html>
var test = 10; //测试用变量
CACHE MANIFEST
# Javascript v0
./cache.js
假设我们现在已经打开浏览器访问http://localhost/blog/html5/cache/mod_cache/cache.html,在打开页面5s左右后,有人修改了cache.js里变量改test的值,如下:
var test = 11; //最初为10,现在修改为11
同时修改了cache.manifest文件(随便在注释里添加几个数字即可)。之前已经设置了20s后,调用update方法检查缓存资源是否更新,此时浏览器发现cache.manifest文件发生变化,且cache.js的确被修改的,于是毫不犹豫地更新了本地缓存。
CACHE MANIFEST
# Javascript v11111
./cache.js
在cache.html里面,将cache.swapCache()这句注释掉了,是否将这句注释掉,是否有差别?差别在哪里?直接看结果:
1、无cache.swapCache(),输出结果:test: 10
2、有cache.swapCache(),输出结果:test: 11
结合API说明不难想到两者之间的差别,当没有调用cache.swapCache()时候,即使重新请求cache.js,加载的还是缓存更新前的cache.js(老的缓存);若调用了cache.swapCache(),则重新请求cache.js时,加载的是已经更新后的cache.js(最新的缓存);
七、写在后面
在离线存储的实现方面,似乎不同浏览器理解也不同,最初在chrome下测试的同时,也在firefox下进行测试,然后,不忍心再对比着测试,就挑了据说实现比较靠谱的chrome接着往下测试
HTML5离线存储应用教程
随着Web App的发展,越来越多的移动端App使用HTML5的方式来开发,除了一些HybridApp以外,其他一部分Web App还是通过浏览器来访问的,通过浏览器访问就需要联网发送请求,这样就使得用户在离线的状态下无法使用App,同时Web App中一部分资源并不是经常改变,并不需要每次都向服务器发出请求,出于这些原因,HTML5提出的一个新的特性:离线存储。通过离线存储,我们可以通过把需要离线存储在本地的文件列在一个manifest配置文件中,这样即使在离线的情况下,用户也可以正常使用App。
怎么用
首先来讲解下离线存储的使用方法,说起来也很简单。只要在你的页面头部像下面一样加入一个manifest的属性就可以了。
<!DOCTYPE HTML>
<html manifest = "cache.manifest">
...
</html>
然后cache.manifest文件的书写方式,就像下面这样:
CACHE MANIFEST
#v0.11
CACHE:
js/app.js
css/style.css
NETWORK:
resourse/logo.png
FALLBACK:
/ /offline.html
离线存储的manifest一般由三个部分组成:
1.CACHE:表示需要离线存储的资源列表,由于包含manifest文件的页面将被自动离线存储,所以不需要把页面自身也列出来。
2.NETWORK:表示在它下面列出来的资源只有在在线的情况下才能访问,他们不会被离线存储,所以在离线情况下无法使用这些资源。不过,如果在CACHE和NETWORK中有一个相同的资源,那么这个资源还是会被离线存储,也就是说CACHE的优先级更高。
3.FALLBACK:表示如果访问第一个资源失败,那么就使用第二个资源来替换他,比如上面这个文件表示的就是如果访问根目录下任何一个资源失败了,那么就去访问offline.html。
浏览器怎么解析manifest
那么浏览器是怎么对离线的资源进行管理和加载的呢?这里需要分两种情况来讨论。
在线的情况下,浏览器发现html头部有manifest属性,它会请求manifest文件,如果是第一次访问app,那么浏览器就会根据manifest文件的内容下载相应的资源并且进行离线存储。如果已经访问过app并且资源已经离线存储了,那么浏览器就会使用离线的资源加载页面,然后浏览器会对比新的manifest文件与旧的manifest文件,如果文件没有发生改变,就不做任何操作,如果文件改变了,那么就会重新下载文件中的资源并进行离线存储。
离线的情况下,浏览器就直接使用离线存储的资源。
这个过程中有几个问题需要注意。
如果服务器对离线的资源进行了更新,那么必须更新manifest文件之后这些资源才能被浏览器重新下载,如果只是更新了资源而没有更新manifest文件的话,浏览器并不会重新下载资源,也就是说还是使用原来离线存储的资源。
对于manifest文件进行缓存的时候需要十分小心,因为可能出现一种情况就是你对manifest文件进行了更新,但是http的缓存规则告诉浏览器本地缓存的manifest文件还没过期,这个情况下浏览器还是使用原来的manifest文件,所以对于manifest文件最好不要设置缓存。
浏览器在下载manifest文件中的资源的时候,它会一次性下载所有资源,如果某个资源由于某种原因下载失败,那么这次的所有更新就算是失败的,浏览器还是会使用原来的资源。
在更新了资源之后,新的资源需要到下次再打开app才会生效,如果需要资源马上就能生效,那么可以使用window.applicationCache.swapCache()方法来使之生效,出现这种现象的原因是浏览器会先使用离线资源加载页面,然后再去检查manifest是否有更新,所以需要到下次打开页面才能生效。
咱们来试试吧
说了这么多,不如自己动手来试试。这里需要说明的是,如果需要看到离线存储的效果,那么你需要把你的网页部署到服务器上,不管是本地还是生产环境服务器中,通过本地文件打开网页是无法体验到离线存储的。
我在我的电脑上跑了一个本地node服务器,通过localhost访问。我的manifest文件向下面这样:
CACHE MANIFEST
#v0.11
CACHE:
lib/ionic/js/ionic.bundle.js
lib/angular-ui-router.js
js/app.js
lib/ionic/css/ionic.css
css/style.css
views/login_header.html
views/login.html
lib/ionic/fonts/ionicons.ttf?v=1.5.2
lib/ionic/fonts/ionicons.woff?v=1.5.2
NETWORK:
lib/ionic/fonts/ionicons.ttf?v=1.5.2
lib/ionic/fonts/ionicons.woff?v=1.5.2
css/style.css
然后我们访问网页看看效果。
可以看出浏览器根据manifest文件下载相应资源并且缓存在本地,现在我们来试试再次访问网页
资源已经离线存储在本地,所以浏览器不需要再次下载资源,可以直接使用本地缓存的资源。接着,我们更新下服务器上的资源,比如我修改下app.js,结果我这里就不显示了,跟上面那张图是一样的,更新的资源并没有生效,现在我们更新下manifest文件,比如把版本改为0.12
很显然,只有更新了manifest文件,对离线资源的更新才能在浏览器上生效。
最后,我们来试试离线状态下是什么情况,这才是离线存储的重头戏。通过Chrome设置离线状态,刷新页面
由于在离线状态,所以浏览器无法访问到manifest文件,但是网页还是可以正常访问,这就是离线存储的威力。
对于HTML5中离线存储对象window.applicationCache有几个事件需要我们关注下:
1.oncached:当离线资源存储完成之后触发这个事件,这个是文档的说法,我在Chrome上面测试的时候并没有触发这个事件。
2.onchecking:当浏览器对离线存储资源进行更新检查的时候会触发这个事件
3.ondownloading:当浏览器开始下载离线资源的时候会触发这个事件
4.onprogress:当浏览器在下载每一个资源的时候会触发这个事件,每下载一个资源就会触发一次。
5.onupdateready:当浏览器对离线资源更新完成之后会触发这个事件
6.onnoupdate:当浏览器检查更新之后发现没有资源更新的时候触发这个事件