黑魔法:断网离线也能看的页面
背景
曾经做过一个Html5的iPhone应用,应用一打开就是一个用webview加载的Html5的页面(现在Apple好像不让这么做了),当时送审这个App的时候,被Apple打回来了,原因是当网络不稳定或者断网离线的时候,App一打开就是个白页或者错误页,Apple绝不接受这种垃圾的用户体验。由于种种原因,我们暂时无法将App做成Native的,于是,我们开始考虑如何让一个Html5页面在断网离线时也能正常显示。
经过技术调研,我们找到了两个可以利用的技术:页面静态化和Html5的离线存储技术,将这两个技术结合,不但可以加速页面的访问速度,而且可以实现断网显示的效果。
页面静态化
这并不是一个新技术,为了提高服务器的响应速度,以及降低服务器的负载,很多Web应用都在使用页面静态化技术。实际上就是用原本动态编译的PHP代码,生成一个静态的html页面。页面静态化特别适合内容相同、访问量大的页面,具体做法如下图所示:
虽然页面上的内容变化的频率不高,但仍有变化的可能性,因此,除了首次请求时生成静态页面外,还可以利用定时任务,自动生成新的静态页面。
或者为静态页面设置过期时间,然后由用户访问来触发,判断静态页面文件是否过期,从而生成新的静态页面。
Html5离线存储
Html5离线存储,或者叫离线缓存,是Html5的一个新特性,利用这个特性,甚至可以制作一个完全离线的Html5应用。当然,我目前的需求还不需要做一个离线应用,但我可以利用这个特性,将一个页面离线在客户端本地。
Html5的离线功能主要包括:离线资源缓存、在线状态监测以及本地数据存储。
- 离线资源缓存: HTML5通过一个扩展名为appcache的manifest缓存资源列表文件,来指明需要缓存的资源。这样,浏览器才能在在线状态时,把这些文件缓存到本地。此后,当用户离线访问应用程序时,这些资源文件会自动加载,从而让用户正常使用。
- 在线状态检测:开发者需要知道浏览器是否在线,这样才能够针对在线或离线的状态,做出对应的处理。在 HTML5 中,提供了两种检测当前网络是否在线的方式。
- 本地数据存储:离线时,需要能够把数据存储到本地,以便在线时同步到服务器上。为了满足不同的存储需求,HTML5 提供了 DOM Storage 和 Web SQL Database 两种存储机制。前者提供了易用的 key/value 对存储方式,而后者提供了基本的关系数据库存储功能。
根据我们的需求,我们只需要利用第一个功能即可。
首先,我们将页面使用的所有资源(主要是图片、CSS、JS等资源),添加到manifest缓存列表文件index.appcache。
CACHE MANIFEST
# This manifest was generated by grunt-manifest HTML5 Cache Manifest Generator
# VERSION 201705111330
CACHE:
# banner
upload/2017_05/14939781424709.jpg?v=201705111330
upload/2017_04/14933720467395.jpg?v=201705111330
NETWORK:
FALLBACK:
然后,我们需要在页面模板上声明一些方法,使得加载页面时可以自动调用Html5的缓存更新机制。
<script>
//添加一个事件监听器,监听页面加载这个动作
window.addEventListener('load', function(e){
//为applicationCache添加一个事件监听器,监听缓存资源的更新状态
//如果缓存资源有更新,就会触发这个监听器
window.applicationCache.addEventListener('updateready', function(e){
//当applicationCache的状态是UPDATEREADY时
if(window.applicationCache.status == window.applicationCache.UPDATEREADY){
//清除本地缓存
window.applicationCache.swapCache();
//更新本地缓存
window.applicationCache.update();
//重载页面
location.reload();
}
}, false);
}, false);
</script>
离线静态页面
虽然我们并没有将静态页面本身加入manifest,但是浏览器仍然将静态页面作为一个资源缓存在本地,并且会在每次请求加载这个页面时与服务端对比是否有变化,如果没有变化,则会直接使用缓存在本地的静态页面文件来提供服务,同时,该页面上的资源也被缓存在了本地。当断网时,由于浏览器检测manifest的请求无法正常收到相应,浏览器就默认资源没有变化,于是,就将离线在本地的整个页面呈现了出来,我们的目的便达到了。
扫描下方二维码或者微信搜索[phpjiagoushier],关注我的微信公众号[PHP架构],参与互动交流。