启动流程
小程序启动需要完成 3 步工作:
●Step1:框架资源准备阶段
●Step2:业务资源准备阶段
●Step3:业务加载渲染阶段
而小程序开发者可以在 Step2、Step3 中来优化性能。
阶段详述
阶段名称 |
含义 |
框架资源准备阶段 |
客户端获取并校验小程序配置、版本、权限等基础信息,完成小程序进程、UC内核、原生UI元素、webview进程、前端框架AppX、基础库等初始化工作 |
业务资源准备阶段 |
下载、解压小程序版本包到客户端中,为业务后续加载渲染准备相应的JS、图片、CSS等资源 |
业务加载渲染阶段 |
读取小程序代码和配置文件,注入到前端框架AppX执行,触发生命周期函数,对用户访问页面进行数据初始化工作,加载页面结构和样式,完成首屏渲染 |
如何优化业务资源准备阶段
概述
阶段定义
下载、解压小程序版本包到客户端的阶段称为业务资源准备阶段。
目的是为业务后续加载渲染,准备JS、图片、CSS等必要资源。
耗时原理
在达到小程序能交互前,客户端会有下载包的过程,即用户进入小程序时看到的 Loading 进度条。客户端会先查询小程序是否有最新版本包,若检测出业务有最新版发版,则会予以下载、解压,存放到本地,为后续加载渲染页面做准备。
在该阶段中,查询过程是异步进行,且耗时极小,在启动耗时中可以忽略,耗时主要体现在版本包的下载及解压过程。小程序包日益加重,下载、解压过程受限于用户网络环境,自身高耗时不说,且后续渲染对其产生强依赖,主文档 index.html 的创建要求等待包下载完成。
根据支付宝性能统计数据,业务资源准备阶段的耗时在整体启动耗时中占比 10% ~ 20%,比例是较高的,但也反馈出若该阶段得到的优化,对整体性能提升将会有巨大的价值。
与更新覆盖率的关系
由于该阶段的特殊性——依赖用户版本更新覆盖率,所以小程序发版后,非即时能达到全量,故在开发者发布小程序最新版本后的,短期内性能有上涨。
不过好在用户更新的过程是一次性的,即更新后不会有二次拉包,后续启动耗时将回落直到稳定,这也正是质量洞察中启动耗时曲线波动的原因所在。
频繁更新发版的小程序,将更频繁的触发用户的包下载、解压动作,致使启动耗时回落速率缓慢
解决手段
了解了业务资源准备阶段高耗时的原因,可以帮助我们整理优化的方向:
1.优化版本包体积,解决单次用户下载包耗时;
影响包体积的因素有离线图片资源、视频资源和JS代码文件,其中离线图片资源优化明显;
2.减少用户下载包频次,控制小程序上架发版频次,解决同一用户多次触发包更新;
对应说明
检测项名称 |
检测项解释 |
解决方案 |
小程序包大小检测 |
包过大会加大小程序包下载耗时,影响业务资源准备阶段,进而影响启动耗时; |
1.删除无用的图片资源及无用代码; |
包内图片大小检测 |
图片大小也会影响包的整包大小,进而影响下载耗时,同时也会影响到图片的解析和渲染耗时 |
1.合理压缩/剪裁图片大小 |
如何优化业务加载渲染阶段
概述
阶段定义
小程序从主文档加载到首屏页面结构、样式全部渲染完成,达到用户可视、可交互状态的过程称为业务加载渲染阶段。
执行步骤
1.主文档加载及视图线程、应用服务线程初始化;
2.业务资源加载及数据、逻辑处理;
3.逻辑层和渲染层的交互,完成首屏渲染;
耗时原理
主文档加载及初始化
小程序主要依靠视图线程(Webview)和应用服务线程(Worker)来控制管理,进入业务加载渲染阶段之前,上述两个线程已创建完毕。
webview创建完成后,将进入主文档的加载,生成 index.html 小程序模板文件;逻辑层框架(Worker)和渲染层框架(Render)开始并行执行初始化过程,此时正式进入业务加载渲染阶段。
衔接之前已下载、解压好的小程序版本包,客户端读取小程序主包的JS代码、css代码和相关配置文件,注入到小程序前端框架中进行编译,编译后的文件分别为 index.worker.js 和 index.js,并分别给到 Worker 和 Render 解析,完成后两者将建连。
上述阶段均由小程序前端框架执行;
对该阶段的影响因素:
●JS代码内容需要给到前端框架编译和解析,JS代码量将影响该环节的时效;
●查看本地前端框架版本,旧有版本会影响该环节的时效;
○升级基础代码库
业务资源加载及数据、逻辑处理
逻辑层再进行必要的数据请求和逻辑处理,如request发送及回拉数据,获取缓存等。
Worker 和 Render 建连完成之后,进入小程序生命周期。Worker将运行 App.onLaunch 和 App.onShow 以完成 App 的创建,再运行 Page.onLoad 和 Page.onShow 以完成 Page 的创建。
前端框架会依照用户访问的页面进行数据初始化的工作,再触发Page.onLoad和Page.onShow 生命周期函数,之后执行网络数据请求、网络资源加载、前端逻辑处理(JSAPI接口)、资源渲染等等业务代码逻辑。
对该阶段的影响因素:
●业务的网络数据请求复杂度、网络资源加载量等;
●JSAPI接口同步调用、JSAPI的并发量等;
逻辑层和渲染层的交互
将逻辑层数据给到渲染层,渲染页面结构和样式信息,完成首屏的渲染。
Render渲染层面需要从Worker逻辑层得到数据,并结合渲染得到的页面结构和样式信息,进行最后的首屏的渲染,展示小程序首屏。
对该阶段的影响因素:
●页面DOM结构复杂度等;
●逻辑层回传到渲染层的数据量等;
对应说明
业务资源加载及数据、逻辑处理
归类 |
检查项Title |
标准说明 |
解决方案 |
网络请求 |
首屏流量消耗 |
首屏页面加载中,会加载和引入图片、音视频和JSON数据等文件,应避免页面加载对用户造成过大的流量开销。 |
1.可考虑资源合并、压缩等技术方案来降低流量开销 |
资源请求数 |
网络请求获取的资源总数量过大,导致网络资源的处理时长增加; |
1.减少不必要的资源加载 |
|
网络请求图片大小 |
网络请求图片过大会影响请求耗时,消耗过多的网络流量。 |
1.合理压缩/剪裁图片大小 |
|
图片并发请求数量 |
短时间内发起太多图片请求会加重网络带宽压力,且并发请求太多也会导致图片加载慢。 |
1.未使用到的图片或者屏幕外的图片采用懒加载处理; |
|
网络请求 |
网络请求耗时 |
单次网络请求(如http/mtop/rpc),从发起请求到数据返回的耗时,网络请求耗时过长将导致数据等待,延迟首屏初次渲染开始的时间,会让用户一直等待甚至离开。 |
1.数据缓存存储网络请求结果; |
网络请求 url 长度 |
受限于系统网络库实现、传输稳定性等影响,单次大数据量的传输,会延迟数据传输时长和处理时长,甚至导致加载失败 |
1.对数据进行合理拆分,分页、分级请求和展示 |
|
接口调用 |
JSAPI调用耗时过长 |
JSAPI调用耗时过长,会影响JSAPI调用性能 |
1.数据缓存存储JSAPI执行结果。 |
同步JSAPI调用次数 |
同步JSAPI的调用过多将造成进程的阻塞,影响后续业务逻辑的执行。 |
1.Sync结尾的同步JSAPI转成异步JSAPI |
|
JSAPI重复调用次数 |
首屏页面加载中,多次调用同一个JSAPI,会增加无用耗时;优化经验中发现,getSystemInfo/getAuthCode/getUserInfo/getNetworkType/request等是重复调用的高发区 |
1.采用数据缓存方式处理前一执行JSAPI后的结果数据,减少调用次数 |
|
JSAPI 并发 |
异步并发过高,可能会导致接口调度减缓,甚至阻塞逻辑;(例如网络通信吞吐量有限的情况下,部分请求会阻塞、超时,甚至直接以失败返回,影响业务) |
1.采用数据缓存方式处理前一执行JSAPI后的结果数据,减少调用次数 |
|
逻辑计算 |
JS函数执行耗时 |
页面打开期间调用的 JS 函数较多,如果多个函数执行耗时都较长,那串行起来会更长,会严重影响用户体验,因此建议对耗时较长的函数进行优化。 |
1.自检代码,关注函数的循环层级,嵌套,以及定时器的延迟问题 |
逻辑层和渲染层的交互
归属 |
检查项Title |
标准说明 |
解决方案 |
页面结构 |
页面AXML节点个数检测 |
一个过大的DOM树会引起内存消耗增长,样式计算时间过长,与复杂的样式规则相结合可能会严重减慢渲染速度 |
1.节点层级超标:减少xxx节点下的嵌套层级,删除冗余节点/重新布局 |
页面样式 |
首屏图片尺寸检测 |
图片太大而有效显示区域较小时会增加内存的消耗,应根据显示区域大小合理控制图片大小 |
1.压缩/剪裁图片尺寸 |
图片分屏检测 |
加载当前屏幕中暂时不需要展示的图片,会影响页面加载耗时,增大内存消耗 |
1.建议分屏懒加载。 |
|
图片大小比例检测 |
图片未按原图宽高比例显示,可能会导致图片拉伸变形,用户识别困难。 |
1.建议根据实际情况设置 image 组件的 mode 属性,以保持原图宽高比。 |
归属 |
检查项Title |
标准说明 |
解决方案 |
数据交互 |
setData数据量检测 |
小程序运行交互时,在逻辑层和视图层的数据传输过程中,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再执行脚本传到视图层渲染。故单次调用setData传递的字符串数据大小需要控制。 |
1.减少数据量,避免一次性setData传递过长的列表。 |
setData调用次数检测 |
首屏页面setData调用次数不宜过多,过多会导致 JS 线程一直在执行编译和渲染,视图层和逻辑层数据交互阻塞,可能用户的事件操作传递到逻辑层不及时,同时逻辑层的处理操作也不能很快的将数据传递到视图层,进而渲染出现延迟; |
1.减少频次,避免频繁触发 setData 或者 $spliceData。 |