摘要 php
为了知足移动终端:节省流量、减小请求、提升客户端性能的需求,咱们设计了webapp安装更新程序,把js、css、html和图片这些资源,序列化为字符串存入客户端本地存储,并带上版本号来实现资源细粒度更新。css
TAGhtml
webapp 安装启动 性能优化前端
咱们认为webapp是一站式的应用,在一个页面里能完成整站的功能。因此,之前经过页面全刷的跳转,如今变成了经过底层框架来支持的局刷和切换动画。为了支持这些功能,会多出很多的代码,再加上app里的功能代码,咱们统称为资源,包括底层库js(zepto、iscroll、baiduTemplate等),通用ui组件和app功能性的js、css、html和图片。web
如何处理一个页面里的这么多资源,才能下降对性能的影响呢?为此,咱们设计了webapp安装更新程序,能够作到减小资源请求,节省流量,提高客户端性能。ajax
使用浏览器对资源缓存,这是一般的作法,能快速加载页面,只需设置资源一个较长的过时时间便可,当服务器端资源有更新时,改变资源路径就能使客户端及时获取更新。json
但缺点是,在webapp应用场景中,有大量的资源须要被下载,资源请求数可能过多,目前正在开发的贴吧webapp宙斯版,已经有60个请求了,预计会过百,固然咱们能够把资源文件合并,但又带来了另一个问题,下载粒度较大。当更新了一个较小部分,会致使大粒度资源被从新下载。因为手机硬件和手机网络的限制,每多一个链接请求的时间消耗和资源下载致使的流量耗费,都是无线webapp要考虑的重要因素。浏览器
另一个缺点就是只能缓存文件,不能缓存一些常量,好比前端模板html片断。缓存
为此,咱们设计的webapp安装更新程序,可以只在一个请求中下载全部资源文件内容。当资源有更新的时候,也是在一个请求中只获取有更新的小粒度文件,而且能在客户端缓存常量,如html片断等。性能优化
咱们想把资源细粒度的存储在客户端,当资源有更新的时候,客户端能及时同步更新。要解决两个主要问题:一、如何在客户端存储资源;二、如何及时获取更新通知,并执行资源更新。
咱们把资源序列化为字符串,以键值对的方式存入客户端本地存储(localstorage)。好比基础库zepto.js文件,在本地存储中的key为zglobal/js/base/zepto.js,value就是zepto.js的被压缩后的字符串内容了。同理,css文件,html前端模板都是同样,适合作成base64编码的图片会以base64字符串的形式存在css文件。
在资源被引用的地方,若是是js文件,咱们会从本地存储中取出js字符串内容,执行eval函数;若是是css文件,咱们会动态建立style元素,包含css内容,追加到HTMLDocument的head部分;若是是html前端模板,就在用到的地方,从本地存储获取前端模板字符串,而后调用模板方法进行渲染。
资源本地存储示例片断:
图一
资源的引用方式sample code:
图二
实现资源更新要依赖于资源版本号。咱们把每一个资源缓存项都对应一个版本号(文件的md5值的前8位),整站全部资源缓存项对应一个总的版本号(全部资源缓存项的版本号的md5值)。
具体流程是:咱们把总版本号存入cookie,在一些入口url的服务器处理程序中,判断客户端cookie是否和当前服务器资源总版本号是否相同,若是相同,则跳过资源更新处理,直接启动app,若是不一样,说明有资源更新,此时,客户端发送一个ajax请求,带上当前客户端每一个缓存项的版本号,向服务器获取更新项,服务器对比客户端每一个缓存项的版本号,就知道客户端哪些资源是须要更新的,哪些资源是被删除了的,所以,服务端就能只返回被更新了的资源。那么,客户端是如何拿到当前客户端每一个缓存项的版本号呢?实际上是依赖于服务器响应客户端ajax资源更新请求时,会包含每一个更新了的资源缓存项的名称、版本号和资源内容字符串,客户端拿到这个结果,会更新本地存储中专门用来记录更新后每一个资源项的版本号的一个本地存储项,以一个json对象序列化后的字符串形式存在,所以客户端发送的ajax请求,就是直接带上这个json字符串便可。
程序流程:
图三
图三中的totalItemVersions就是全部细粒度资源缓存项的版本号记录。在客户端本地存储中的key为totalItemVersions,value是{“zglobal/html/frsMostTemplate.html”:”d6670285″,”zglobal/js/base/ajaxStore.js”:”68ce034f”,”zglobal/js/base/appBaseController.js”:”4083dd27″,”zglobal/js/base/appBaseModel.js”:”f1f806f1″,”zglobal/css/common/app.css”:”66153242″,”zglobal/css/common/base.css”:”2731733d”……….}
经过以上,咱们能发现,当服务器端无资源更新时,启动的webapp将不会有网络资源请求,直接加载本地资源,而后启动app,后续只是app动态的json数据交互;当服务器有资源更新时,无论被更新的资源的多少,客户端都是经过一个请求获取仅更新的资源,而后加载资源,并启动app。
了解了前面整个客户端和服务器的交互以后,咱们可能会关心,这些资源在服务器端是如何生成并存储的?这依赖于一个本地build过程。
在贴吧的前端开发中,是按不一样模块来开发的,不一样模块包含不一样的功能,这样能减小你们同时修改带来的冲突。最后的上线,也是按修改的哪些模块,只上线相应的模块修改便可。
每一个模块下都会有本地build脚本,只负责本模块的build工做。对于安装更新这块来说,本地build输出两个文件,moduleVersion.php和cache.php。
cache.php就是当前模块下全部的资源缓存项,每一个项的version就是资源文件的md5值的前8位,cache.php文件以下:
图四
moduleVersion.php表示当前模块的版本号,文件以下:
图五
模块版本号的计算方法是当前模块下全部缓存项的版本号的总链接字符串的md5的前八位。这样当任何一个资源文件有修改的话,资源文件的version将会改变,moduleVersion的值也会改变。
在“获取资源更新通知”一节中,提到了整站全部资源缓存项的总版本号,这个版本号的计算方法是,全部模块的版本号的总的链接字符串的md5。
图六
若是你们也在作无线webapp,建议不妨试试这种资源处理方式,将会有更少的资源请求,而且让资源更快加载,给用户带来更好的体验。
by gaofei