极致的 Hybrid 混合式开发

4,439 阅读9分钟
原文链接: yq.aliyun.com

该文章来自于阿里巴巴技术协会(ATA)精选文章。

IO 优化

今天  去啊 App 5.1.1 已经发布了,航旅 Hybrid 混合架构有了更进一步的落地,这轮优化目标是搞定H5真正的“无缝秒出”。

先来看效果,去啊App 2G 网络下购买国际机票,30秒完成,除了搜索入口页,列表往下到付款页的前端都是H5 Page,大家感受一下:

be4c2c9b4fa8d3e0d2e7ec92df37403a.gif

目测性能是可观的。尽管H5包的离线化隔绝了弱网对秒出的干扰,设备本地 IO 耗时也是不能忽视的,算上zip的解压缩,仅在设备本地 IO 完全一个 HTML(包含其携带的资源文件)也会达到秒级的耗时,在iphone5上也会有0.5秒左右的白屏时间。

另外,HTML 本身的优化依然无法绕过,WebView 里 JS 的运行效率通常是 Mobile Browser 里的四分之一。所以 JS 必须尽可能靠后的介入渲染,CSS 必须尽可能靠前的介入渲染。而在当下前端开发习惯来看,页面通常会至少阻塞加载一个种子文件(比如kissy),而在Mobile设备中,哪怕是无干扰Dom的JS阻塞运行都会带来0.3秒左右的白屏延时。

所以,H5容器的IO优化和HTML渲染提前是本期改造的重点,即:

  1. H5 容器加载资源的 IO 优化:干掉zip包的即时解压,离线包更新后立即解压散列到SD卡,每次创建 View 时直接从设备读文件,本地文件管理依然基于标准的H5包规范。
  2. HTML 本身的页面优化,CSS 提前,JS 置后。

“CSS 提前和JS置后”的动作是由工具来完成。即只要构建工具统一,开发者不需要关心如何摆放脚本最优,只需关注实现业务逻辑即可,举个例子,JS 置后的做法是这样:

其实无非是将同步的脚本改成异步。先保证带样式的页面框架秒出来,然后去执行 JS。从性能数据上甚至看不出差别,但从感官上的提升非常明显,我宁愿让你先看到界面,而不是白屏。

IO 优化(续)

从策略上,做到这两点足够应付大多数场景,能保证页面任何时候能秒出来。但在我们整个 Hybrid 架构中,我们设计了比较灵活的 H5 离线化,即用虚拟域来确保在线离线的一致,比如酒店团购H5页面,浏览器扫码打开是一个在线页面,用去啊App扫码进来就是一个离线页面,同一份代码,一个走网络加载,一个走本地加载:

这样设计主要是为了做线上线下的开关切换,紧急问题可随时配置到线上页面,线上页面是可以随时部署的,离线页面则涉及到推包。为了做这层开关,就会带来判断文件是线上还是离线的问题。

因为H5容器虚拟域的代理层会引导 WebView 去本地而不是网络读文件。如果读不到本地文件,再创建一个HTTP请求去对应的线上请求文件。如果通过 IO 一次再判断本地是否存在文件,显然实际的 IO 次数依然降不下来(所有的资源文件请求都必须要 IO 一次手机硬盘),为了降低实际 IO 次数,我们针对每个 H5 离线包生成一个本地资源文件列表(cache_info.json),并同时将域名路径和文件路径做成了映射 Map,这个映射表读入内存后备用,涉及到H5的访问,用这个 Map 过滤下就能直接找到要读的文件位置,Map 匹配不上就直接创建HTTP从网上取文件了,这样可以避免无必要的 IO。

AngularJS 在移动端的加速

去啊 Hybrid 中存在大量的列表页,比如上面的团购和视频中国际机票的例子,AngularJS 是最适用于这种场景。但 AngularJS 目前没有专门针对 Mobile 做优化,而在 Mobile 设备里,hashchange带动数据模型data-model的变更,进而通过View来渲染。首次渲染稍慢,因为要等Model准备好才出视图,无论如何界面也秒不出来(从hashchange到出界面至少0.8 秒)数据结构在稍微复杂点,立即破1秒,当然这个时间是伴随着首次加载构建DOM的干扰。

几点改造:

  1. AngularJS 种子文件异步载入
  2. 非数据关联的HTML一律预装在页面
  3. 首次加载干掉任何影响页面抖动的 HashChange
  4. 基础数据的离线化(比如城市列表)

这几点无非是让首次渲染能尽快的展现,接下来 OPOA 中的编程就是 AngularJS 所擅长的了。

离线包里的 TMS 如何载入

我们知道 AWP 只支持静态 HTML 的发布,这没什么好解释的,毕竟,高性能要求更简单粗暴且有效的缓存,全静态就可以全缓存,尤其是在移动终端的破网里面,缓存尤其重要,但 TMS 动态内容如何生成?我们都知道 AWP 平台提供了一个<!--HTTP:url-->的标签(使用说明),TMS 变更后就重新生成一份HTML 推送到CDN上。

那么问题来了,离线包里怎么做?

我们团队的同学写了offline-tms-parser模块,clam会将<!--HTTP:url-->标签构建:

<!--HTTP:http://abc.php,gbk:HTTP-->

构建为:

<script 
    src="http://trip.taobao.com/market/trip/h5_offline_service.php? callback=handle_tms_fragment&src=http%3A%2F%2Fabc.php " 
    charset="gbk "></script>

这样,在离线包里的 TMS 内容就和 H5 页面完全分开了,毕竟 TMS 由于变更频繁,是没办法直接做离线化的。但上面这段脚本显然会阻塞页面渲染,所以offline-tms-parser提供了异步的版本,即将:

<!--HTTP:http://abc.php?async,gbk:HTTP-->

构建为:

<script id="tms_fragment_1
                                                            ">
get_tms_fragment("http://abc.php ", "gbk ", "tms_fragment_1 ");
</script>

然后在 H5 页面顶部塞一个get_tms_fragment的实现即可。

如此,基于 AWP 平台开发业务将不受任何影响,工具帮我们处理好 TMS 和 H5 页面的百分百解耦,离线包里也可以大量使用 TMS 了。

尽管如此,也必须不能滥用 TMS,代码逻辑层面的 View 和 Data 的解耦仍然是必须要做的。

让人期待的 grunt-kmb

本期优化面向 H5 离线容器的代码改造的重点是上面这几项,但还有一项激动人心的变化: grunt-kmb,没错,构建流程也需要瘦身和简化了。

grunt-kmb是团队另一位同学正在开发的 grunt-kmc 的替代方案。相比于 grunt-kmc 基于正则匹配的模块解析,grunt-kmc 完全基于 uglify-js 提供的 JavaScript 语法解析树(AST),先看对比:

grunt-kmc 的速度:

grunt-kmb 的速度:

整整一倍的提速,太让人垂涎期待了。

What's Next

本期的优化没有多少高科技的东西,大都是体力活,将技术做透。好的体验真的是靠点滴的积累,需要花时间去磨。

其实从去啊App Hybrid 刚开始设计并做第一版优化到现在已经有半年多时间,这期间离不开客户端同学的全心投入,因为 H5 的优化必须借助高性能的 WebView 容器,但靠 H5 或者 Web 技术本身无论如何是无法做到秒出的。再配上《高性能网站建设指南》里零碎的优化技巧,H5 在 Hybrid 中完全可以做到秒出,再配备离线包动态推送和埋点采集等基础设施,Web 技术的瓶颈在 Mobile 中就能获得一定的突破。

我在这次集团 前端技术峰会上的汇报里,提到过我们面临的挑战:

去啊 App 是介于工具化的钱包App和运营化的手淘App之间,既有营销活动,也有工具化、流程化的 PageFlow。将标准化的 PageFlow 的性能做到极致,接下来的挑战就是如何将运营活动也做到智能的推送,完成这类页面的离线部署。

当然,活动页面的离线化部署目前来看不是我们面临最急迫的难题,最棘手的是数据打通的问题:

看这个场景,在钱包App做了一个引流的入口,唤起了去啊App,并定位到机票搜索结果页:

目前我们很难跟踪唤起的效果,手机里的App是信息孤岛,很难通过简单的传参来把跨 App 的 PageFlow 串联起来。即使参数能带过来,但身份信息、登陆状态和账号关联能否也能带过来呢,每一项都是很有挑战。

我在前两天给航旅的新人培训上,分享了 我理解的《Mobile First》,这段话是值得分享给大家一起共勉的:

内聚的 App 和散列的 Web 似乎是一个不可调和的矛盾。企业倾向于认为只有提供越来越多地功能,才能满足用户不断膨胀的需求,但又会打破原有应用的简便易用。当两者无法调和,信息膨胀到必须散列到不同的端时,通过 Web 技术将这些信息孤岛关联起来就显得至关重要。

所以,我们之前所做的所有技术的优化,都是在为构建无线“前端/终端”技术体系夯实根基,所以这一轮无线All In 从某种意义上看是一种原始积累,第二轮无线All In才会迎来真正的非标商品的个性化、多元化时代。这时我们的技术体系将着重解决这几类问题:

  1. 千人千面,重点解决面向不同人群做手机里的定制化功能的问题
  2. 无线大数据,重点解决打破App间的信息孤岛,让跨端的数据化运营更加高效、可靠
  3. 无线开源,让 B 商家参与进来,像手淘一个(Native)商品详情页通吃所有商品的时代很快就会被更个性、多彩的非标商品打破,这时,大量垂直领域的产品模式设计更多要靠商家参与,而不是我们自己那几个产品经理,所以,更开源、更自由的手机店铺,闭环服务才是王道。

说到闭环,去啊的机票购买流程,是无线端闭环的经典案例:

这个例子是从钱包的入口开始购机票流程,通过短信、Push、下载、唤起等操作,将用户捣腾到去啊App里(整个过程体验流畅,估计你也很难分清楚哪些Page是H5的、哪些是Native的吧)。

So,开源 + 闭环,是我理想中的无线 Hybrid 技术的终极状态,去啊 Hybrid 技术积累也才刚刚开始。