HTML5离线存储利与弊

一、概念

离线存储是HTML5中的一个重要特性,顾名思义就是将一些资源文件保存在本地,这样后续的页面重新加载将使用本地资源文件,这样子使得你的web应用可以在用户离线的状况下进行访问,很显眼有三个好处:

  1. 最直接的好处就是用户可以离线访问你的web应用
  2. 因为文件被缓存在本地使得web页面加载速度提升许多
  3. 离线应用只加载被修改过的资源,因此大大降低了用户请求对服务器造成的负载压力

二、如何实现离线文件存储

首页你的服务器得先支持html5,先来看看html5的离线文件存储应用对你的代码有什么要求,你需要在页面的html标签中通过manifest属性引用一个manifest文件来使得你的应用可缓存。简单地说,manifest文件是一个文本文件,它罗列了离线访问应用时所需缓存的文件清单(注意:引用该manifest文件的页面,不管你有没有罗列到清单中,都会被缓存),如图:

 

 

需要在服务器上配置对应的的MIME类型信息,该文件所对应的mime-type应该是text/cache-manifest的,配置如图,发个PHP配置的

接下来看下manifest文件的结构,很简单,如图:

 

CACHE:
 这个是manifest文件的默认入口,在此入口之后罗列的文件 (或直接写在CACHE MANIFEST后的文件)在它们下载到本地后会被缓存起来
NETWORK:
 可选的,在此节后面所罗列的文件是需要访问网络的,即使用户离线访问了也会直接跳过缓存而访问服务器
FALLBACK:
 可选的,用来指定资源无法访问时的回调页面。每一行包括两个URI,第一个是资源文件URI,第二个是回调页面URI。

备注:以上描述的这些节是没有先后顺序的,而且在同一个manifest中可以多次出现

有些坑是需要注意的:

1).路径写法不支持带端口,所以最好用相对路径吧,要不然线下测试机上开发,有端口不好调试。

2).*号通配符有时候好像并不好使。如果在某些设备上出现问题,可以排查一下是不是这个导致的。

3).#以后的内容虽然是注释,但是不能放到第一行,第一行必须是如上的大写字母。

综上所述,其实就三个步骤  1,配置服务器manifest文件的MIME类型;2,编写manifest文件;3,在页面的html标签的manifest属性中引用写好的manifest文件。

三、离线后的页面

页面离线后,好处显而易见,页面可以秒开了(启动速度快)。你会感觉页面加载的速度快了很多。但是也有一些问题。

A.页面都离线了,如何更新?首先,你可以修改下manifest文件来更新这个页面,这个我就不介绍了,但是作为文章内容页面离线以后,就会存储在本地了,如果你是一篇文章的话,那么这个文章的内容页就被存下来了,你如果以相同的url去访问,不管你文章里面的数据更新没有,这个离线下来的页面都不会更新了(除非你更新manifest文件)。所以,所有的动态数据,你都得用ajax方式去获取,就像客户端一样,离线的页面应该是一个没有数据的空壳,然后通过ajax去拉去数据填补这个空壳。然后要注意的是,ajax的请求地址,要写到manifestnetwork中,要不然,你可以试试。

B.页面的参数如何携带?还是刚才那个问题,文章页的壳如果缓存了,怎么样传数据过来标识这个特点页面呢?比如m.cp.360.cn/app?a=1&b=2,通常我们用一些参数来标记这个页面,通过参数来渲染页面内容,但是manifest对于上面的方式,会认为不同的参数表示不同的页面。如果你吧内容页做成一个无数据的空壳,这种传参的方式显然不行,好在不一样的hash页面,manifest会认为是同一个页面,比如m.cp.360.cn/app#detail-111111与m.cp.360.cn/app#detail-222222会认为和m.cp.360.cn/app是同一个缓存页面。这样我们就可以通过hash传值了(也就是现在流行的单页面体验,这块可以易讯彩票和QQ电影票触屏版及淘宝彩票的部分页面),当然,你也可以通过其它方式传值,比如写入cookie,写入localstorage方式等等。

C.如何计算PV UV? 由于当前页面被强制加入manifest,那么PV 和UV的统计,成了一个难题,因为请求不再是发送到服务器;(一个常规的方案是进入离线存储页面后自动发出ajax请求,以告知服务器统计PV UV;)

四、离线页面的更新

当用户本地再次联网的时候,本地的离线存储资源需要检查是否需要更新,这个更新过程,也是通过manifest的更新来控制的,更新了manifest文件,浏览器会自动的重新下载新的manifest文件并在下一次刷新页面的时候进行资源文件的重新请求(第三次刷新替换本地缓存为最新缓存),而且这个请求是全局性的,也就是所有在manifest缓存列表中的文件都会被请求一次,而不是单独请求某个特定修改过的资源文件,因为manifest是不知道哪个文件被修改过了的。

1).长尾问题,就像前面说到的一样,如果你的manifest文件更新了,你访问页面,需要刷新一次,更新的页面才能load进来,那么这样就有一个问题,如果你的后端数据,就是给js ajax接口的数据变化了,你对应的js也修改了。那么你修改manifest上线的时候,第一次开页面,页面就会bug了。再刷一次页面,就好了。那么,这个第一次访问的bug,是我们不想看到的。而且你不能知道用户什么时候第二次再来访问你的页面,所以你的页面一旦使用manifest离线,就像客户端一样,这样就出现了长尾问题。还好,manifest有一些js接口,可以来判断,load更新文件。

2).刷新页面

API一段代码:

  1. 1) 当前文档对应的applicationCache对象 
  2. window.applicationCache
  3. 2) cache.status 属性,返回当前离线应用的状态
  4.        UNCACHED (数值0):未启用离线应用
  5.        IDLE (数值1):已开启离线应用,但本地缓存的资源是最新的,并且未标记为废弃资源
  6.        CHECKING (数值2):当前更新缓存的状态为检查中
  7.        DOWNLOADING (数值3):当前更新缓存的状态为下载资源中
  8.        UPDATEREADY (数值4):当前更新缓存的状态为更新完毕
  9.        OBSOLETE (数值5):已开启离线应用,但缓存资源都已标记为废弃
  10. 3)事件处理器:
  11.   事件名称接口执行时机下一步将执行
  12. checkingEvent检查是否需要更新,或者在第一次下载manifest文件时。 最先执行的事件noupdate, downloading, obsolete, error
  13. noupdateEventmanifest文件未修改,不需要更新Last event in sequence.
  14. downloadingEvent需要更新缓存,或者第一次下载资源时progress, error, cached, updateready
  15. progressProgressEvent下载资源中progress, error, cached, updateready
  16. cachedEvent资源已下载完成,并且已完成缓存最后的事件
  17. updatereadyEvent资源更新完毕,并且可以用swapCache()来启用新的缓存。最后的事件.
  18. obsoleteEvent加载manifest文件时遇到401404错误,所以缓存将被删除。最后的事件.
  19. errorEvent加载manifest文件时遇到401404错误,将中断缓存网页。最后的事件.
  20. 4) 常用api
  21. window.applicationCache.update()  //update方法调用时,下载资源到本地如果没有cache需要更新,则抛出 INVALID_STATE_ERR 异常
  22. window.applicationCache.swapCache()  //updateready后,更新到最新的应用缓存更新缓存到最新的资源如果没有新的资源,则抛出INVALID_STATE_ERR异常并不会使本次加载的页面立即重新加载资源,仅有在执行该方法后刷新页面才能看到最新的资源。

有了jsapi,一切都好办了,我们可以干很多事情了。现在你可以用js来判断页面的状态了,可能你已经想到,判断状态后,可以刷新一下页面,那么,就算数据发生了修改,这种处理方式也是ok的,没错,这样确实解决了问题。你也可以在设置了manifest的页面中加入这些方法,然后修改下文件,看看会触发哪些事件,刷新页面的唯一问题就是页面会闪动一下,这点体验当然是不好的。因此,我们想到了loading页面。

  1. var appCache=window.applicationCache;
  2. console.log(appCache.status);
  3. appCache.addEventListener(“obsolete”,function(){
  4.        console.log(“Obsolete,status:”+appCache.status);
  5.    },false);
  6. appCache.addEventListener(“cached”,function(){
  7.        console.log(“chache,status:”+appCache.status);
  8.    },false);
  9. appCache.addEventListener(“checking”,function(){
  10.        console.log(“checking,status:”+appCache.status);
  11.    },false);
  12. appCache.addEventListener(“downloading”,function(){
  13.        console.log(“downloading,status:”+appCache.status);
  14.    },false);
  15. appCache.addEventListener(‘noupdate’, function(){
  16.        console.log(‘Noupdate,Status:’ + appCache.status);
  17.    }, false);
  18. appCache.addEventListener(‘updateready’, function(){
  19.        console.log(‘updateready,Status:’ + appCache.status);
  20.    }, false);
  21. appCache.addEventListener(“error”,function(){
  22.        console.log(“error”);
  23.     },false);

3).Loading页面

制作一个loading页面,用来检测manifest文件有没有更新如果发现有更新,则更新资源,然后再跳到真实的首页。这个实现起来就比较容易了,在loading页面你可以加一些效果,比如加载效果等等,给用户一个预知。让我们看看manifest修改过和没有修改过各种状态的差别。保证你的manifest文件存在有效,如下:

  1. CACHE MANIFEST
  2. #test11123aaadfsaasdadffdsfaaaffd
  3. http://s5.cp.360.cn/mobile/static/v3/js/lib/libs.js?v1.0.0.js 
  4. testb.html
  5. NETWORK:
  6. *

Html文件如下:

  1. <!DOCTYPE html>
  2. <html manifest=”notes.manifest”>
  3. <head>
  4.     <title>离线存储demo</title>
  5.     <meta http-equiv=”Content-Type” content=”text/html;charset=UTF-8″>
  6. </head>
  7. <body>
  8. Hello world
  9. <script type=”text/javascript” src=”http://s5.cp.360.cn/mobile/static/v3/js/lib/libs.js?v1.0.0.js”></script>
  10. <script type=”text/javascript”>
  11.    var appCache=window.applicationCache;
  12.    console.log(appCache.status);
  13.    appCache.addEventListener(“obsolete”,function(){
  14.        console.log(“Obsolete,status:”+appCache.status);
  15.    },false);
  16.    appCache.addEventListener(“cached”,function(){
  17.        console.log(“chache,status:”+appCache.status);
  18.    },false);
  19.    appCache.addEventListener(“checking”,function(){
  20.        console.log(“checking,status:”+appCache.status);
  21.    },false);
  22.    appCache.addEventListener(“downloading”,function(){
  23.        console.log(“downloading,status:”+appCache.status);
  24.    },false);
  25.    appCache.addEventListener(‘noupdate’, function(){
  26.        console.log(‘Noupdate,Status:’ + appCache.status);
  27.    }, false);
  28.    appCache.addEventListener(‘updateready’, function(){
  29.        console.log(‘updateready,Status:’ + appCache.status);
  30.    }, false);
  31.     appCache.addEventListener(“error”,function(){
  32.        console.log(“error”);
  33.     },false);
  34. </script>
  35. </body>
  36. </html>

如果页面没有发生变化:那么以上代码会输出:

如果对应的manifest有修改,需要更新文件,那么上面的代码会输出:

如果更新了manifest,会触发downloadingupdateready事件。你可以根据这两个事件来处理一些逻辑了,比如在downloading给用户一些提示,正在加载,updateready就跳转到真正的页面,那么这个时候,页面就已经加载好了,就不用再刷新才生效页面了。比如:

  1. appCache.addEventListener(‘updateready’, function(){
  2.        console.log(‘updateready,Status:’ + appCache.status);
  3.        location.href=”testb.html”;
  4.    }, false);

该页面在loading页面上,其实主要的内容页面在testb.html上。

4).Loading页面的问题

这时,你可能认为,这个不错,解决了所有问题。效果也ok,也没有第一次加载页面可能出错的状况。如果对于一个单页面来说,这样确实没问题了,但是如果你是一个完整的站,问题又来了,你不可能给网站每个页面都加loading,就算每个模版页都用一个loading,如果用户进入了你某个页面,如果把你某个页面存到了桌面快捷方式。那么下次启动,可能还是会挂,必须刷一次页面才能好,所以,你可能想到,整个页面都要通过jsbuild了……,然后又要保持build页面的js要在页面渲染前发生更改,那么,你可以每个壳模版页面判断manifest的逻辑,然后动态载入的js,通过动态载入的jsbuild整个html页面,那么这样就木有bug了,不过这种处理方式确实恶心了,如果你的模版页的html壳更新不多,仅仅会更新里面的js逻辑的话,那你可以在改html壳中动态载入js即可,也可以直接刷一下页面来解决。

但是这毕竟不是一个app,如果你的站点突然接到需求要更新得很频繁,比如一天要更新一次,那么,这个manifest离线的意义就仅仅在于第二次载入的时候可以秒开。除此之外,我也想不到其他好处。然后用户如果一天刚好访问一次的话。这个状态好像还没有不用manifest离线的状态好。那么,我需要把manifest下线……

五、适用场景

上线,下线 ,长尾等等问题都考虑过了,现在我们应该可以正确使用manifest了,现在来讨论一个问题,什么样的页面适合使用manifest呢。关于这点,目前我们还没有具体的数据证明,但是通过manifest离线的页面,第二次访问时启动速度快了很多,而且,如果处理得当,还是有一些收益的,每次更新manifest文件以后,manifest中未修改的文件是不会被重新下载的,会返回304,而且页面中的其他静态文件,如css和img等,也能享受到传统缓存的方式。

如果你的应用是一个html5的游戏、或者是一个htmlapp应用,明显,采用manifest会收到很好的效果,第二次及以后加载速度都非常快,而且你的应用如果不依赖网络的话,就算用户断网了,也可以正常使用,如html5游戏,你完全可以离线来带来更好的用户体验。然而对于一些传统的站点,也想运用一下manifest新特性,如果你的站点每天都有上线更新,每天都有上线,从manifest的机制上来说,这个是不怎么适合用manifest离线的,静态资源本来就可以自己缓存,引入manifest还会带来一些维护成本已经上面提到的一些问题。但是你的站点开发完成以后,更新频率比较低,做完一版以后很长时间内不做升级(这点类似客户端),那么,你的站点就很适合用manifest离线来优化用户体验,manifest虽然带来了一些好处,但是也有很多问题。

 

相关文章: 

        HTML5离线存储入门:http://i.wanz.im/2010/07/01/html5_offline_file_cache_guide_for_beginner/ 

               HTML5离线存储:http://blog.csdn.net/quzishen/article/details/7319703

        Application Cache: http://www.cnblogs.com/blackbird/archive/2012/06/12/2546751.html

        慎用manifest: http://mweb.baidu.com/p/%E6%85%8E%E7%94%A8manifest.html

               HTML5离线存储的有趣问题: http://www.cnblogs.com/ylguailei/archive/2012/04/16/2446204.html

posted @ 2013-09-29 11:37  qgd87  阅读(546)  评论(0编辑  收藏  举报