小程序启动性能优化实践
一、小程序简述
小程序作为新型的 APP 应用解决方案,由于它快捷的开发效率,高效的发布能力,以及丰富的与端协同能力,被越来越多的被互联网各个厂商所认同,不约而同的转战到这个赛场上。性能是推动业务飞轮的技术突破点,不免也会引发大家对小程序的性能这一块的疑虑?小程序的性能如何衡量,有没有比较完善的针对性能优化的思路与工具?针对上面的问题,我们从性能优化的价值、性能指标的定义域衡量以及如何优化性能这几部分内容来帮助大家快速对小程序性能优化相关的方法有一定的理解。
二、为什么要进行性能优化
小程序性能是指小程序在百度 APP 或其它宿主 APP 中加载和呈现的速度,以及小程序对用户交互的响应程度。性能欠佳的小程序显示速度和对输入响应的速度较慢,甚至会出现内容不可访问的情况,这将在不同程度上影响用户体验,从而导致用户流失。
提升使用时长
开发者希望用户能够通过小程序进行有意义的行为。例如电商类小程序希望用户购买商品;内容类小程序希望用户阅读、订阅;政务类小程序则希望更加简单便捷的为用户提供服务。
多项案例研究表明好的性能,可以更好的留住用户:
Pinterest 将用户等待时间减少 40% 后,搜索引擎流量和注册人数随即增长 15%;
COOK 将平均加载时间减少 850ms 后,转化率提升 7%、跳出率下降 7%,同时每次会话浏览页数增加 10%。
而性能欠佳则将流失更多用户:
BBC 研究发现网站加载时间每增加 1s 将多失去 10% 的用户;
DoubleClick by Google 则表示,如果页面加载时间超过 3 秒,53% 的移动网站访问活动将遭到抛弃。
提高转化率
转化率是指用户进行了某些目标行为的访问次数与总访问次数的比率,这些行为包括订阅、购买等。
留住用户对于提升转化率至关重要,已有研究表明缩短渲染时间可提高转化率:Tokopedia 将 3G 连接的呈现时间从 14s 减少到 2s 后,访问者增加了 19%,会话总数增加了 35%,新用户增加了 7%,活跃用户增加了 17%。同理,缩短小程序渲染时间,将有效提高转换率。
提升用户体验
性能是创造良好用户体验的基本要素。当用户进入小程序时,良好的性能可以快速加载页面。如果性能欠佳,加载速度过慢,用户则不得不等待,当用户忍受低性能的小程序到一定程度后,则会选择放弃。《High performance iOS Apps》一书中的数据显示, 25% 的用户在应用启动时间超过 3s 时会放弃使用。
三、性能指标与衡量
使用性能指标来评估小程序的加载速度是非常必要的。首先回顾一下小程序页面加载的几个关键阶段。
这几个阶段的含义分别如下:
阶段 | 含义 |
Loading | 下载小程序包阶段 |
First Paint(FP) | 界面的首次绘制 |
First Contentful Paint(FCP) | 首次有内容的绘制 |
First Meaningful Paint(FMP) | 首次有意义的绘制 |
Time to Interactive(TTI) | 页面绘制完成,达到可交互状 |
我们最为关心的是,从用户点击小程序到第一个页面首次有意义绘制(FMP)的总耗时。
百度智能小程序为开发者提供了性能监控指标平台,帮助开发者监控小程序的线上加载性能。开发者可以在“开发者平台-开发管理-运维中心”的界面,看到小程序的加载性能监控。
在运维中心中,用户可以监控小程序的性能指标(在运维中心中,FMP 称为上屏时长),如下图所示:
四、如何进行性能优化
4.1 小程序启动流程
有效地识别小程序性能问题并制定优化策略的重要前提是能够了解、熟悉小程序启动流程。
因此,我们首先大致介绍下百度智能小程序的启动流程,如下图所示:
接下来简要介绍下各个环节小程序框架主要做的内容:
1、用户点击打开小程序
小程序启动的起点。
2、下载小程序包
用户首次打开小程序时会下载最新版本的小程序包。
3、启动小程序运行框架
初始化小程序逻辑层
加载逻辑代码:逻辑代码指的是App、页面、自定义组件和用到的其他js模块的集合。
执行 APP 生命周期函数:必要的 js 资源加载完成后,小程序框架会执行 App.onLaunch、App.onShow 生命周期函数。
初始化小程序渲染层
加载当前页面显示所需要的多种资源,包括:app.css、页面和所有自定义组件的模板(.swan)与 css 相关文件等。
自定义组件资源加载完成后,小程序框架会初始化自定义组件,将其处理为渲染所需要的组件。
4、initData 与首次渲染
逻辑层初始化完成后,小程序框架会收集页面显示所需要的数据(initData),然后将此数据发送给渲染层(sendInitData)。
渲染层收到 initData 后,会初始化页面,绑定模板与数据,并开始首次渲染(FCP)。
5、执行页面生命周期
渲染层完成页面首次渲染后,会通知逻辑层(firstPageRender)开始执行页面生命周期。
开发者通常会在 onLoad-onReady 生命周期中,开始请求页面主数据(小程序希望首屏展示给用户的数据)。
6、首次有意义的渲染
数据请求完成后,开发者通常会将数据发送给渲染层(setData),然后渲染层绘制完成后触发 FMP。
后文中,我们会把页面主数据的请求和之后对应的 setData 称为核心请求(request)和核心 setData。
4.2 如何识别性能问题?
性能问题可能会发生在启动流程中的不同阶段。
不同的小程序,面向的使用场景不同,业务逻辑不同,展现给用户的页面结构也不同,因此性能问题的表现会有巨大的差异。
即使对于同一小程序,不同阶段存在的问题对最终性能的影响程度也不同。我们往往需要关注性能瓶颈,而忽视相对不重要的部分。
此外,小程序启动流程中的各个阶段并不是独立的,会存在一些联系。当完成一部分性能优化后,性能问题的表现可能会和之前存在较大的差别,需要重新分析考虑。
因此小程序性能问题的分析与识别是一个相对复杂的过程。过去,分析小程序性能问题,通常根据过往的性能优化经验或者通过阅读小程序代码等方式来做到。但这一过程不仅耗时耗力,且最终结果不一定准确。
工欲善其事,必先利其器。使用好的工具,会极大地提高我们分析、解决问题的效率。
百度智能小程序提供了启动性能分析工具(https://t.hk.uy/bbsQ),可以将启动流程形象化地展示出来,以帮助开发者快速定位性能问题与影响。
后文中,有时会将启动性能分析工具简称为性能工具。
上图为启动性能分析工具的展示页面,主要由三个部分组成:顶部栏、左侧栏与主面板。
顶部栏中央是两个选择器,分别用于选择历史性能数据和对应的不同页面的性能数据。左侧栏用于切换不同的主面板,以显示不同类型的性能数据分析结果。
在 FMP 面板中,启动性能分析工具将启动流程转化为了时序图:
1、左侧为逻辑层,右侧为渲染层
2、从上到下,时间逐渐增加
3、线的长度,表示了这一阶段在整体启动流程中的耗时比例(相对值)
时序图的右侧,通过表列出各个阶段具体的开始、结束与耗时情况(绝对值)
4、绿线表示当前线程忙碌,灰线表示空闲,左右侧连接线表示通信(事件、数据传输),蓝线表示请求。
可以通过时序图,快速判定哪个阶段,相对而言是最耗时的。
4.3 如何解决性能问题?
不同的性能问题,对应着不同的解决办法。
根据经验,小程序的性能问题,通常出现在初始化小程序逻辑层、请求主数据(onLoad 执行)、首次有意义的渲染这几个阶段中。下文将描述这些性能问题的具体表现与相应的优化策略。
小程序包优化:减少小程序初始化耗时
在时序图中,通常体现为在 App.onLaunch 前耗时过长,如下图所示:
在 app.onLaunch 前,主要阶段为加载和执行逻辑代码。
加载逻辑代码的时长,主要和小程序逻辑代码体积相关。
一般而言,小程序包越大,逻辑层代码体积也越大,因此加载会越耗时。
开发者可以使用分包、独立分包(https://t.hk.uy/bbsS)或剔除无用代码等形式,降低小程序包的代码体积。
执行逻辑代码的时长,主要和小程序逻辑代码业务相关。
除了优化 js 代码本身执行的耗时外,开发者也需要关注 swan API 的调用情况。
很多 swan API 因为涉及到与客户端 APP 的通信,因此比 js 代码执行更加耗时。而小程序可能会重复调用很多 API,但这些 API 的调用结果是不变的,例如 getSystemInfoSync 等。因此建议根据业务情况,将结果进行缓存,会有效减少执行逻辑代码的时长。
请求提前:尽可能早发出核心请求
很多开发者会在 onLoad 中发出核心请求。但如启动流程所示,onLoad 执行依赖于首次渲染完成,如果首次渲染较慢,则会导致 onLoad 执行过晚。
百度智能小程序提供一种页面级别的生命周期Page.onInit。此生命周期会在页面首次渲染前触发,相较于 onLoad,生命周期执行时间会有极大的提升。
下图为将请求提前至 onInit 的时序图效果:
可以看出,请求响应与首次页面渲染是并行处理的。因此会有非常明显的优化效果。
百度知道、百度百科、宝宝知道小程序进行此优化后,分别带来了 210ms、100ms、150ms 的性能提升。
开发者要接入 onInit,可参考:Page.onInit 接入指南(https://t.hk.uy/bbsT)。
请求响应优化:减少核心请求耗时
在时序图中,体现为核心 request 蓝线过长:
除了优化网络服务器本身的性能外,优化请求响应也可以从如下几方面考虑:
开启 prelink
用于提前和业务服务器建立网络连接,使随后的主请求可以复用该连接,提升请求速度。
上图为使用 prelink 前后的对比:在配置了prelink时,小程序框架会在加载逻辑代码的同时发送一个预连接请求。在发送主请求时,可以直接复用该连接,达到减少主请求时长的目的。
prelink 配置指南(https://t.hk.uy/bbsW)
开启服务端 gzip
gzip 是节省带宽和加快数据速度传输的有效方法。当服务端开启 gzip 后,可以减小请求传输的体积,使得传输速度更快。
减少请求响应的体积
某些情况下,请求响应中包含了较多与页面显示无关的数据,导致了传输体积过大,响应耗时太长;例如响应体积有 200K,其中只有 50K 与页面显示有关。
包含过多无用数据的原因可能是因为一个数据接口服务于多种需求,而每种需求需要的数据不尽相同,因此导致了体积过大。
可以考虑与服务端约定新的数据格式与接口,以减小请求响应体积。
渲染优化:避免首次渲染较多内容
如下时序图所示,请求完成后,核心 setData 的渲染成为了最耗时的阶段。
通常而言,这意味着开发者渲染了过多的内容。
在很多业务场景中,小程序渲染的页面的高度是超过一屏的。但在首次进入页面时,只需渲染出可视范围的内容即可。当页面首次渲染完毕后,再继续异步渲染剩下的页面内容。因此可以对首次渲染的内容做一定取舍或删减,例如:
如果页面从上到下,由多个不同的独立部分组成,那么不在首屏内的部分,可以稍后渲染。
如果页面是由一组结构相同的多个列表项构成,那么首次渲染时,主动控制列表项的长度,不在首屏中的列表项,稍后渲染。
案例参考:宝宝知道问答页按需渲染优化(https://t.hk.uy/bbsY)
五、总结与展望
开发者在了解小程序的性能指标和启动流程之后,可以比较容易地通过启动性能分析工具,发现性能问题并进行优化。除了上述提到的性能问题与优化策略外,开发者也可以进一步阅读百度智能小程序官方提供的性能优化文档,获取更多、更详细的优化方法与实践案例。
除了根据时序图开发者自行分析外,目前性能工具同时提供了性能分析清单,可以自动分析出小程序的性能问题并给出优化建议。检查项与建议,基于小程序当前运行的结果与线上的统计数据,融合了小程序性能优化的实践经验,因此通常会更加直观有效。
在未来,我们会尽可能提前将可能存在的性能问题提示给开发者:将自动化的性能分析与小程序发布流程有机地结合在一起,将性能问题拦截在全量发布前,避免到了上线后才发现改动会引起非预期的性能退化。
---------- END ----------