当前移动端和前端的结合愈加紧密,尤其是在偏重活动运营的电商App中,受制于App版本审核,具备研发成本低、可灵活发布等特点的H5页面受到青睐,使其在APP端上承接了越来越多的业务。然而H5页面本身也存在一些亟需解决的问题,列举如下:
H5页面给人最直观的感受,便是打开速度明显是比Native要慢,也就是用户体验问题;
H5页面的实际表现,譬如白屏发生率、加载错误率等,需要端上进行系统化的性能监控;
H5页面在某些复杂的功能或交互的实现上,受到APP端上能力的限制,从而降低了其开发效率;
如果业务方需要一个全新的纯H5页面App(通常用于内部ToB使用),依然需要移动端团队来提供具备端上能力的壳容器APP,这个开发流程对于前端同学来说是不自由的。
通过分析上述H5页面存在的一些痛点,可以简单的将其归为两大主题:用户体验和研发效能,H5容器化建设也是围绕这两大主题展开。那么作为承载Web的容器,APP端上需要具备哪些能力来提升H5页面的用户体验和研发效能呢?思路如下:
针对用户体验问题,其一是通过端上的能力,尽可能的帮助H5页面缩短加载过程中的耗时;其二是搭建一套完整的端上H5页面性能监控体系,实时掌控其实际表现。
针对研发效能问题,其一是在APP端上提供丰富的内置JSAPI,同时将这些能力进行细粒度地分类、并组合成多个插件,供不同的APP以最低成本接入;其二是让前端同学也具备一键生成APP的能力,即通过提供一些必要的配置信息,达到直接输出两端APP安装包的效果,在开发流程上极大摆脱对移动端开发资源的依赖。
综上所述,本文将以移动端开发的视角,聚焦加载提速优化、性能监控体系、功能插件化、壳容器APP工厂化这四个切入点,来详细介绍APP端上的H5容器化建设。
以发展的眼光来看,APP端上页面,无论是Native还是H5,动态化能力和性能体验的融合将会是大趋势,针对H5页面的性能体验优化相当于补齐木桶的短板。移动互联网时代,用户对于网页的打开速度要求越来越高。百度用户体验部研究表明,页面放弃率和页面的打开时间关系如下图所示。
要做性能体验的提升,首先简要分析H5页面的完整加载流程:
用户点击打开一个独立的H5页面,Native页面路由+WebView初始化,此时页面无法交互;
用户到达新的页面,页面所需资源下载中,此时处于白屏状态;
页面基本框架出现,从Server端获取业务接口数据,此时页面处于loading状态;
如何缩短这些过程的时间,就成了H5页面加载提速优化的关键。接下来我们将逐一分析这几个阶段的耗时情况,以及在端上可以实施的优化点。
首先分析H5页面加载过程的第一阶段,即无法交互阶段,该阶段包含了WebView初始化的过程。我们发现,当App首次打开时,默认并不会初始化浏览器内核;而只有当创建WebView实例的时候,才会创建WebView的基础框架;因此,启动浏览器内核的时间开销要被统计到用户首次打开H5页面的耗时中。当WebView首次初始化后,一些WebView共用的全局服务或资源对象仍没有释放,因此第二次打开H5页面会变快。
首次H5页面打开平均时间 | 经过预热后首次H5页面打开平均时间 | |
---|---|---|
耗时 (单位ms) | 1554.6 | 1044.1 |
在APP启动后,并且空闲时,预先创建 Webview 对象池,将容器预载在内存中,需要使用时直接获取池中的对象。用一次创建多处复用的方式降低内存抖动,同时提升H5页面的打开速度。
这种做法的本质是用内存换时间,因此可以针对"不爱打开H5页面"的用户,有选择性的进行容器预热,从而尽量避免额外的内存开销。对于如何给用户打上"不爱打开H5页面"的标签,可以根据不同的产品特性进行不同的策略处理。
继续分析H5页面加载过程的第二阶段,即白屏阶段。通常来说,加载一个H5页面最耗时的部分就在于页面资源的下载,如html、css、js以及部分图片文件等,受手机性能、网络状态等因素影响,该阶段耗时从几十毫秒到几千毫秒不等。如果这些文件不需要下载,那么剩下的时间开销基本就是业务数据的下载以及页面的解析渲染了。离线包方案就是为了降低资源下载过程的耗时。
首先构建Admin后台管理系统,主要帮助开发人员对离线包进行配置管理,主要包括资源的版本管理、总容量管理以及资源发布等。
XCache Server负责提供资源的下载接口,CDN作为资源镜像,在Server接口连续失败后提供备用接口。
APP端上在合适的时机拉取资源配置表,继而对照新老配置表对缓存到本地的资源进行增删改,同时对已下载的资源做MD5校验,以保证资源的实时性和安全性,并基于LRU策略对本地缓存统一管理。
在用户触发H5页面的访问时,WebView实时拦截资源请求,判断是否命中本地离线包资源,从而提高加载速度,在弱网环境下优化效果会更加明显。
将资源命中率、异常监控等信息上报,从而协助发现问题、实时调整策略。
提速效果明显
首先是提速方面的优化,根据线上AB实验,在同一个落地页HTML的加载速度有150ms的提升,页面总体的load时间平均耗时降低了200ms。同时,优化效果在前端团队的监控指标中也得到了呼应,该页面白屏时长分布在100ms以内的占比从原来的28.4%提升到了71.2%。总体来说提速效果非常明显。
CDN省流
自上线以来,已经有2000余次的资源更新发布,根据线上命中率统计,每年离线包将为CDN节省流量超过数百TB。
副作用不明显
由于资源的处理已经被置于较低的优先级,通过线上AB实验,对APP冷启动、CPU使用率、以及内存使用率等几个指标的监控,证明离线包方案对APP运行的副作用几乎可以忽略不计。
业务指标的提升
页面因为白屏更短,降低了万分之24的用户离开率。我们经常讲"技术赋能业务",这个案例就是一个比较好的佐证,用户终归对体验还是有一定要求的——“如果你太慢,我就是不想等”,因此端上的用户使用体验总是在各个细节影响着用户的决策。
最后分析H5页面加载过程的第三阶段,即loading阶段。在无法改变后端业务接口速度的前提下,如何比原来更快的收到业务数据,便成了端上优化的一个切入点。如果说离线包提速方案解决的是H5资源文件下载慢的问题,那么数据并行加载就是以提高业务数据获取速度为目的的优化。
以线上某个H5页面的加载过程为例,从图中看出,整个H5的加载流程是串行的,业务接口必须在H5资源ready、以及js环境ready之后才会发出;而如果在H5页面被打开的同时,移动端协助H5提前将业务请求发出,并维护该数据在内存中,最后通过JsBridge的方式将数据提供给H5,H5便不需要再发出请求,从而利用并行特性,缩短H5的白屏时间。
该方案同样需要类似于离线包的后台管理系统,负责配置表的管理和发布。
APP端上在合适的时机拉取配置表,获取待优化页面的url信息、接口信息;然后,端上需要对一些H5接口特有的Cookie信息进行请求和管理。
当APP端上感知到待优化页面被打开时,主动拉取并保存H5侧的业务数据,用hash(url+api)作为该数据的key,并约定H5侧通过Jsbridge以相同的key来获取数据。同时对数据的生命周期进行管理,比如在退出H5、数据过期时对其进行清理。
将优化命中率、异常监控等信息上报,从而协助发现问题、实时调整策略。
轻量级
由于数据并行加载是在用户点击进入H5的那一刻才开始进行,而不影响其他业务场景,所以整体来说较轻量级。
H5页面呈现速度更快
每次命中优化都可以为前端节省50ms-300ms的耗时,线上落地页面首屏在1200ms内完整呈现的达标率较对照组平均提升9.8%。
从体验的维度来看,除了加载提速优化,另一个重要专题就是性能监控体系的建设,本质上是通过监控H5页面的实际表现来辅助开发人员发现问题、解决问题,最终的目标仍然是提升用户体验。
前端的性能监控天生被局限在WebView的生命周期内,能力的边际是受限的;而在移动端上做H5页面的性能监控,能够从更高的维度、更广的视角获取更多的信息。下图罗列了性能监控所需要的一些指标:
页面加载耗时是最能反应用户真实使用体验的指标,也是最重要的监控指标。
下图描绘了H5页面的加载过程中,移动端/前端各自能感知到的监控时间点。其中,黄色部分为移动端的监控点,以Android端Native & WebView
的事件回调为基准进行绘制;白色部分为前端的监控点,以W3C标准中Performance Timing
参数为基准进行绘制。
前端监控的局限性
对于耗时监控的合理开始时间点,从图中可以看出,在APP端初始化阶段,前端没有办法获取到任何信息。如果单纯依赖前端的navigationStart
作为起始点,那么监控数据会明显低于用户的实际感知耗时;而移动端可以准确获取容器页面的初始化时间点,严格一点甚至可以从feed流中点击item开始统计。
移动端监控的局限性
对于耗时监控的合理结束时间点,移动端通常会在页面全部加载完毕之后作为节点。以Android端为例,即WebViewClient#onPageFinished
的回调时间点。然而通过追溯onPageFinished
的回调时机,发现其必须满足:页面解析完毕、 没有正在下载的资源等条件。按照这个标准,一旦存在某个图片一直处在加载中,但页面框架的其他内容均已处理完毕,onPageFinished
也会等待图片加载完成才回调,这将比用户的实际感知耗时更长。相对应地,前端在 domInteractive
时间点,已完成页面展示所必需资源的请求和处理,更贴近用户对于网页加载完成的实际体验感受。
双端协同的耗时监控方案
结合上述分析,可以确定:最准确的页面加载开始时机来自移动端;最准确的页面加载完成时机来自前端。因此,完善的耗时监控需要双端协同完成,方案如下:
移动端获取WebView初始化时间点,作为耗时监控开始时间点;
移动端通过JavaScript注入获取上述W3C Performance Timing
中的domInteractive
时间点,作为耗时监控结束时间点;
最后由移动端完成加载耗时的计算,并统计上报。
H5页面白屏问题是最影响用户体验的问题之一,需要特别关注。因此希望对该场景进行精准的检测与问题定位,从而需要一套完整的白屏监控系统。
已有的基于前端实现的白屏检测方案有很明显的弊端:受限于WebView的生命周期,前端白屏检测无法覆盖到容器异常、资源加载异常等情况,统计的白屏率比真实的线上情况偏低;而端上的白屏监控不仅准确率更高,定位问题的方法和手段也更多。整体的方案设计如下图所示:
基于像素点抽样的检测模块
传统的白屏检测方法,即在端上截图并统计所有白色像素点的数量占比是否大于阈值。由于图像中的像素点在色值梯度上具有一定程度的连续性,因此并不需要遍历所有的像素点,而可以采用固定间隔的抽样方式作为替代。当抽样率sample
设置合理时,白色像素点的数量占比计算结果误差较小,却可以有效降低检测过程中的内存开销和耗时开销。
基于JSBridge通信协议的误判纠错模块
在一些布局简单的H5页面中,存在展示内容少、白色背景占比较大的情况,这些白色像素点的数量占比很可能超过预设阈值threshold
,从而发生白屏检测误判,这也是线上出现过的情况。
该模块就是为了解决上述误判问题:在H5页面完成加载之后,前端侧需要通过JSBridge通信协议调用原生页面的特定方法,使得H5页面可以主动通知检测模块自身的页面渲染处于正常状态,确定当前H5页面肯定不为白屏,从而降低检测的误判率;若上述特定方法没有被检测到调用,则说明当前H5页面可能处于异常状态,继而进行后续的检测流程。
基于页面现场信息的上报模块
本模块负责将H5页面白屏发生时的一些现场信息上报服务器,包括APP端上的一些信息,如用户机型、系统版本、当前网络状态信息、内存使用情况、渲染进程信息、reload
信息等;同时也包括当前H5页面的一些信息,如url地址、关键DOM节点信息等。其中DOM节点信息可以通过端上注入JavaScript脚本获得。
基于上报信息的白屏归因模块
在检测到白屏问题时,首先需要明确是哪端的问题,才能安排相关同学进行深入排查,否则责任的归属不明,也就很能推进问题的解决。因此本系统根据上报信息对白屏问题进行了初步归因,汇总如下:
上报信息中的一些现象总结 | 问题端 | 白屏问题初步归因 |
---|---|---|
端上可用内存较少的问题凸显 | APP端/前端 | 内存占用过多 |
在某个特定机型、或特定系统版本中出现 | APP端 | 机型/系统兼容问题 |
端上onConsoleMessage 异常回调 |
前端 | JS错误或兼容问题 |
关键DOM节点数据异常/端上网络状态较差 | APP端/后端 | 网络资源加载失败 |
端上onRenderProcessGone 异常回调 |
APP端/前端 | 渲染进程异常 |
端上reload 页面后从白屏恢复到正常 |
APP端/后端 | 网络波动导致 |
端上的网络环境信息,通常被作为日常问题排查过程中的基础参考信息;不仅如此,还可以作为业务风控相关的参数信息。网络环境监控信息罗列如下:
网络属性信息
包括运营商信息、IP信息、网络类型等;
网络状态信息
包括信号强度、网络可用性、服务器长时间无响应、是否被劫持、是否使用代理、是否开启VPN等。
最后,针对H5页面,在端上展开优化思路和实施的同时,需要同步建立衡量优化效果的性能指标,以及过程中的异常信息监控,从而协助发现问题、实时调整优化策略。
优化命中率 & 优化收益
以线上案例分析说明:在离线包提速方案中,后台资源配置发布更新之后,通过数据面板发现旧版本的资源依然存在缓存命中的情况,导致新版本资源的缓存命中率不及预期。通过复现分析,为了防止用户在流量资费方面的投诉,离线资源的下载时机配置为仅Wi-Fi条件下进行,导致部分用户在Wi-Fi条件下拉取了旧版本资源,后续在4G/5G环境下使用,便忽略了资源的更新事件,从而导致了问题的出现。继而通过细化配置,即仅在资源本地存在旧版本且需要更新迭代时放开网络环境限制,最终解决问题。
过程中的异常信息监控
以线上案例分析说明:在数据并行加载提速方案中,通过异常信息的上报监控,发现配置表的获取接口失败率较高,超过10%,导致后续的优化策略无法执行。通过沟通、分析,定位到该服务接口基于node实现,鲁棒性较差,最终将配置表发布到CDN,解决了配置表异常的问题。
以上两个线上案例均证明了对优化方案进行监控的必要性,这也是"成果量化、结果导向"方法论的重要体现。
业界在混合开发实践中,H5开发在面临复杂业务功能和交互时,往往需要Native能力的支持。为了解决H5与Native之间的双向通信,JSBridge通信机制的应用愈发成熟,Native端提供的JSApi也越来越丰富,成为了一套成熟的、可供H5开发进行快速业务迭代的技术方案。
上述开发模式在单APP的业务场景下没有什么问题,但是在移动端团队负责多个APP的开发和维护时,便出现了诸多不便:
容器本身没有独立抽离,导致新的App必须重写H5容器以及与之相匹配的JSBridge通信协议,效率低下。
容器能力的可移植性较差,同样的功能,多个宿主App需要多次重复实现。这不仅影响H5端在新APP中的业务迭代速度,Native端多个工程相同JSApi代码的同步维护也是一件非常繁琐的事情。
容器能力没有根据特性分类,全部平铺到工程里。举个例子,比如Native提供给H5的相册选择能力与社交分享能力,前者属于系统层面功能,后者则属于业务层面功能。若不分类,将不利于后续进行细粒度的容器能力插件化。
为了解决背景中的问题,基于移动端的H5容器能力插件化方案应运而生。方案的本质是将Native提供给H5的能力细粒度拆分,独立成插件,各自维护;进而宿主APP不需要关心插件能力的实现细节,可以根据自身业务场景的需求,低成本、最小化接入。
容器能力插件化的首要任务,就是将这些能力根据其特性进行梳理、分类,介绍如下:
容器核心层
主要包括容器性能监控插件(用于宿主APP对容器实际表现的监控)、容器通信协议插件(与H5侧通信的基础设施)、容器核心节点代理插件(用于宿主APP对容器核心节点的回调做出处理)。
容器性能优化层
主要包括针对H5容器的提速优化功能插件,宿主APP可按需接入,具体内容已在本文第三章介绍,不再赘述。
容器业务能力层
该层包含了所有和实际业务相关的插件,共6个,宿主APP可按需接入,分别是基础外观插件、基础信息能力插件、系统通用能力插件、系统媒体能力插件、用户相关能力插件、业务相关能力插件。具体的能力可见上图中的分支明细。
基于移动端的H5能力插件化方案的总体架构如上图所示,容器层依赖Native能力层实现自身能力,APP层可以根据自己的业务场景需求,对容器层选择性接入;同时,容器层本身也被独立抽离,同步维护,不需要再游离在多个APP工程中。
总结一下本方案的收益:
容器使用成本更低
容器层单独抽离,宿主App可实现一键接入;同时,容器能力统一维护,已有能力不需要在新APP工程中重复开发,也从侧面提高了H5页面的开发效能。
容器能力细粒度插件化
容器能力通用性强,且覆盖面广,包括了所有和实际业务相关的通用能力、提速优化能力、监控代理能力等;宿主APP均可按需接入具体的某个插件,真正做到H5容器能力的细粒度插件化。
在上一节中的H5能力插件化建设中,我们可以看到宿主APP对于H5容器的所有需求,几乎都可以得到满足;然而却忽略了一种特殊应用场景,即宿主APP本身都不存在的情况。
在部门内部的ToB业务发展过程中,诞生了很多仅供内部员工使用的运维类APP;和面向千千万万大众的用户不同,这类APP的使用对象不是一个人,而是一个岗位。使用者的核心诉求是更好更快地完成自己的工作内容,而对APP的体验要求相对不高,再加上Native APP的双端实现成本较大,因此根据经验,移动端团队仅需提供一个壳APP、加上内部H5容器提供底层服务支持,前端同学即可完成ToB APP中的页面开发以及动态发布。开发流程如下图所示:
从流程中可以明显的看出问题:
方案的本质是让前端具备生成APP的能力,从而在纯H5页面的APP中,使得前端最大程度地摆脱对移动端的依赖。这就需要移动端提供一个可供前端使用的黑盒系统,系统输入必要的APP工程配置信息以及容器能力配置信息,输出Android/iOS两端的APP安装包,即像一个工厂一样输出可定制化的壳容器APP。
基于移动端的壳容器APP工厂主要职责
流水线作业,首先处理前端的配置输入,确保包含生成APP所必需的配置项;
基于Native模版工程进行脚本化的文件遍历修改,内容包括APP名称、图标icon、版本号、首页H5页面地址、包名等必要信息;
基于移动端的H5能力插件化方案对目标工程中的H5容器能力进行定制选择;
基于打包脚本对目标Native工程进行代码编译、打包,并最终将Android和iOS两端的安装包输出给前端。
方案收益
综上所述,该方案借鉴了前端工程脚手架的思想,通过提供必要的配置项,使得前端可以一键生成承载H5页面的壳容器APP,并包含了与其业务相匹配的H5容器能力,优化了ToB APP的开发流程,大幅提升了前端开发的自由度和研发效率。
本文从移动端开发的视角出发,以用户体验和研发效能两个核心问题作为引导,分别从加载提速优化、性能监控体系、功能插件化、壳容器APP工厂化四个角度,详细介绍了APP端上的H5容器化建设过程。从发展的角度来看,H5容器化建设是一个长期的、综合性的工作,有许多细节可以深入考虑,未来可以根据业务需求不断升级体验优化策略,根据开发痛点不断提高研发效能;同时可以结合服务端的优秀算力,做出更多突破大前端职能边际的技术方案。
Performance Timing Information - W3C
H5 容器简介 - 蚂蚁技术团队
WebView性能、体验分析与优化 - 美团技术团队
页面加载速度的重要性 - 百度技术团队
QQ音乐Android客户端Web页面通用性能优化实践 - 腾讯技术团队