启动性能

小程序启动是小程序用户体验中极为重要的一环,启动耗时过长会造成小程序用户流失。

本章节的启动只包括小程序冷启动,不包括小程序后台切前台的热启动。

一、启动流程

在进行启动优化之前,先简单介绍以下小程序的启动过程。在小程序启动过程中,主要包括以下几个方面:

  • 注 1:小程序启动的这些部分不是串行完成的,会尽可能的并行进行。
  • 注 2:小程序启动各部分不是每次启动都完整进行的,会尽可能的利用缓存。

1. 资源准备

1.1 小程序相关信息准备

在用户访问小程序时,微信客户端需要从微信后台获取小程序的配置、版本、权限等相关信息,以对小程序进行必要的版本管理、权限控制和校验等。

对启动耗时的影响

信息的获取和更新会影响小程序的启动耗时,尤其体现在用户首次访问小程序时。

为了在尽量降低影响启动耗时的情况下保证信息的实时性,这些信息会在本地缓存,并通过一定的机制定期进行更新。

1.2 小程序运行环境准备

在执行小程序代码之前,微信客户端需要准备小程序运行的基础环境。

小程序的运行环境包括小程序进程、原生部分的 UI 元素(如 导航栏、tabBar等)、渲染页面使用的 WebView 容器、运行开发者 JS 代码的 JS 引擎、小程序基础库等等。

对启动耗时的影响

运行环境的准备耗时较长,对小程序的启动耗时有明显影响。

为了尽可能的降低运行环境准备对启动耗时的影响,微信客户端会根据用户的使用场景和设备资源的使用情况,提前对运行环境进行部分地预加载。但由于受到设备资源和操作系统调度的影响,目前很难保证每次小程序启动时都有预加载的环境。

1.3 小程序代码包准备

小程序启动时需要从服务器获取代码包地址、下载小程序代码包,并对代码包进行校验。根据小程序页面所在分包和使用的插件不同,一次启动可能需要下载多个代码包或插件包。

当小程序版本发生更新时,微信客户端还需要对代码包进行增量更新。

对启动耗时的影响

下载耗时是启动耗时中的重要瓶颈,与代码包或增量包的体积正相关。微信采用 ZSTD 算法对小程序代码包进行压缩,以尽可能降低下载过程中传输的数据量。

考虑到包大小对用户体验的影响,平台限制单个代码包的大小上限为 2M。代码包上限的增加对于开发者来说,能够实现更丰富的功能,但对于用户来说,也增加了下载流量和本地空间的占用。为了保证启动速度,开发者应该尽可能的控制代码包大小。

2. 开发者代码注入(逻辑层)

小程序启动时需要从代码包内读取小程序的配置和代码,并注入到 JS 引擎中。在主包代码注入过程中,会触发小程序的 App.onLaunch 和首次 App.onShow 生命周期。在开发者代码注入完成后,框架侧会根据用户访问的页面进行一些页面数据初始化工作,触发首页的 Page.onLoad, Page.onShow 事件。

对启动耗时的影响

开发者代码的注入耗时直接影响小程序的启动耗时。在主流的 JS 引擎中,代码注入耗时包括编译和执行等环节,代码量、同步接口调用和一些复杂的计算,都会影响注入耗时。

由于首页渲染需要使用逻辑层发送的数据,如果开发者代码注入耗时过长,也会延迟首页渲染开始的时间。

在部分平台上,微信客户端会使用 V8 引擎的 Code Caching 技术对代码编译结果进行缓存,降低二次注入时的编译耗时。

3. 开发者代码注入(渲染层)

开发者的 wxss 和 wxml 会经过编译注入到渲染层,包含页面渲染需要的页面结构和样式信息。渲染层的注入耗时主要和页面结构复杂度和使用的自定义组件数量有关。

渲染层和逻辑层的开发者代码注入是并行进行的。

对启动耗时的影响

由于首页渲染需要使用渲染层的页面结构和样式信息,如果开发者代码注入耗时过长,会延迟首页渲染开始的时间。

4. 首页(初次)渲染

在开发者代码注入完成后,结合逻辑层得到的数据和渲染层得到的页面结构和样式信息,小程序框架会进行小程序首页的渲染,展示小程序首屏,并触发首页的 Page.onReady 事件。Page.onReady 事件触发标志小程序启动过程完成

对启动耗时的影响

首页渲染耗时主要受页面结构和参与渲染的数据量影响。

二、关键概念

在讨论小程序启动耗时时,需要明确几个概念:

1. 小程序首屏渲染完成

从开发者角度看,小程序首屏渲染完成的标志是首页 Page.onReady 事件触发。

从框架的角度来说,小程序的首屏的内容是基于小程序的初始数据,以及在渲染开始前到达的 setData 数据渲染的。

首屏渲染完成不表示小程序页面一定有完整内容,开发者触发的 setData(例如通过 wx.request 异步请求数据)不一定能参与到首屏渲染中。

由于框架和启动流程的差异,小程序定义的首屏渲染完成不等同于浏览器的 load 事件。

2. 小程序启动阶段

从用户点击访问小程序到小程序首屏渲染完成(首页 Page.onReady 事件触发)。

3. 打开率/到达率

小程序首屏渲染完成 PV 与 用户点击访问小程序 PV 的比值。流失率 = 1 - 打开率

三、启动性能优化

在启动流程中,小程序代码包准备、开发者代码注入和首页渲染是与开发者相关的,开发者可以进行一定的优化工作。其他部分目前开发者暂时无法干预,框架侧负责进行持续的优化。

1. 小程序代码包优化

代码包优化的核心手段是降低代码包大小,代码包大小直接影响了下载耗时,影响用户启动小程序时的体验。

开发者可以采取以下手段优化代码包大小:

1.1 分包加载

使用 分包加载 是优化小程序启动耗时效果最明显的手段。建议开发者按照功能划分,将小程序的功能按使用频率和场景拆分成分包,实现代码包的按需加载。

分包加载具有以下优势:

  • 承载更多功能:小程序单个代码包的体积上限为 2M,使用分包可以提升小程序代码包总体积上限,承载更多的功能与服务。
  • 降低代码包下载耗时:使用分包后可以显著启动时需要下载的代码包大小,在不影响功能正常使用的前提下明显提升启动耗时。
  • 降低开发者代码注入耗时:小程序启动时会一次性注入全部的开发者代码,使用分包后可以降低注入的代码量,从而降低注入耗时。
  • 降低页面渲染耗时

此外,结合分包加载的几个扩展功能,可以进一步优化启动耗时:

分包预下载

在使用「分包加载」后,虽然能够显著提升小程序的启动速度,但是当用户在使用小程序过程中跳转到分包内页面时,需要等待分包下载完成后才能进入页面,造成页面切换的延迟,影响小程序的使用体验。分包预下载便是为了解决首次进入分包页面时的延迟问题而设计的。

独立分包

但小程序中的某些场景(如广告页、活动页、支付页等),通常功能不是很复杂且相对独立,对启动性能有很高的要求。独立分包可以独立于主包和其他分包运行。从独立分包中页面进入小程序时,不需要下载主包。建议开发者将部分对启动性能要求很高的页面放到特殊的独立分包中。

独立分包和分包预下载可以配合使用,获得更好的效果,详情请参考独立分包与分包预下载教程

1.2 代码重构和优化

通过代码重构,降低代码冗余。在使用如 Webpack 等打包工具时,要尽量利用 tree-shaking 等特性去除冗余代码,也要注意防止打包时引入不需要的库和依赖。

1.3 控制代码包内图片等资源

避免在代码包中包含或在 WXSS 中使用 base64 内联过多、过大的图片,应尽量采用网络图片。代码包内的图片一般应只包含一些体积较小的图标。声音、视频等其他类型的资源应尽量避免放到代码包中。

小程序代码包在下载时会使用 ZSTD 算法进行压缩,降低下载时传输的数据量。这些资源文件会占用大量代码包体积,并且通常难以进一步被压缩,对于下载耗时的影响比代码文件大得多。

1.4 及时清理没有使用到的代码和资源

在日常开发的时候,我们可能引入了一些新的库文件,而过了一段时间后,由于各种原因又不再使用这个库了,我们常常会只是去掉了代码里的引用,而忘记删掉这类库文件了。

目前小程序打包是会将工程下所有文件都打入代码包内,也就是说,这些没有被实际使用到的库文件和资源也会被打入到代码包里,从而影响到整体代码包的大小。

2. 开发者代码注入优化

开发者代码注入的优化可以从优化执行耗时和优化代码量两部分着手。

2.1 减少启动过程的同步调用

在小程序启动流程中,会注入开发者代码并顺序同步执行 App.onLaunch, App.onShow, Page.onLoad, Page.onShow。在小程序初始化代码(Page,App 定义之外的内容)和启动相关的几个生命周期中,应避免执行复杂的计算逻辑或过度使用Sync结尾的同步API,如 wx.getStorageSyncwx.getSystemInfoSync 等。对于 getSystemInfo, getSystemInfoSync 的结果应进行缓存,避免重复调用。

2.2 使用懒注入

通常情况下,在小程序启动时,启动页面所在分包和主包(独立分包除外)的所有JS代码会全部合并注入,包括其他未访问的页面以及未用到自定义组件,造成很多没有使用的代码注入到小程序运行环境中,影响注入耗时和内存占用。

自基础库版本 2.11.1 起,小程序支持仅注入当前页面需要的自定义组件和当前页面代码,以降低小程序的启动时间和运行时内存。开发者可以在 app.json 中配置:

  1. {
  2. "lazyCodeLoading": "requiredComponents"
  3. }

注意:添加这项配置后,未使用到的代码文件将不被执行。

3. 页面渲染优化

3.1 提前首屏数据请求

大部分小程序在渲染首页时,需要依赖服务端的接口数据,小程序为开发者提供了提前发起数据请求的能力:

  • 数据预拉取:能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度。
  • 周期性更新:在用户未打开小程序的情况下,也能从服务器提前拉取数据,当用户打开小程序时可以更快地渲染页面,减少用户等待时间。

3.2 骨架屏

在页面数据未准备好时(如需要通过网络获取),尽量避免展示空白页面,应先通过骨架屏展示页面的大致结构,请求数据返回后在进行页面更新。以提升用户的等待意愿。

3.3 缓存请求数据

小程序提供了wx.setStoragewx.getStorage等读写本地缓存的能力,数据存储在本地,返回的会比网络请求快。如果开发者基于某些原因无法采用数据预拉取与周期性更新,我们推荐优先从缓存中获取数据来渲染视图,等待网络请求返回后进行更新。

3.4 精简首屏数据

我们推荐开发者延迟请求非关键渲染数据,与视图层渲染无关的数据尽量不要放在 data 中,加快页面渲染完成时间。

四、启动性能分析

1. 性能数据获取

1.1 小程序助手「性能分析」板块

小程序管理后台的性能数据还没有更新,正在规划改版中,建议在改版完成前开发者以小程序助手的性能数据为准。

建议开发者使用小程序助手中「性能分析」板块,持续关注小程序性能。性能分析从 启动性能、运行性能和网络性能 三个维度提供性能数据,供开发者根据平台、机型、网络环境和访问来源等条件做精细化分析。扫描下方小程序码即可立即体验。

启动性能 - 图1

目前,小程序助手中只包括微信客户端 >= 7.0.3 版本的正式版小程序数据。

1.2 通过 API 在小程序内获取

开发者可以在小程序中根据自身业务需有进行性能打点,也可以使用wx.getPerformance,获取当前小程序性能相关的信息。

在获取信息后,开发者可以自行上报或使用 wx.reportPerformance 进行测速。

1.3 体验评分

小程序工具中提供了体验评分工具,方便开发者能够及时发现小程序的体验问题。

2. 影响启动性能的因素

根据「启动流程」一节介绍的启动流程来看,影响小程序启动耗时的因素众多,分析小程序启动性能是一个比较复杂的过程。

对于同一个小程序,以下因素会直接影响平均启动耗时:

  • 平台: 不同平台下(安卓、iOS、PC等)设备性能、操作系统、框架实现、优化方案存在较大差异,启动耗时也存在较大的差异。只有分平台比较启动耗时(包括各阶段的耗时)才有意义
  • 下载比例: 代码包下载和更新都会显著影响小程序启动耗时,在其他流程耗时稳定的情况下,下载比例升高会影响大盘启动耗时。
  • 入口页面:不同页面启动时,根据所在分包的不同,需要下载的代码包数量和大小和代码注入量都存在差异。不同页面渲染耗时也存在差异。
  • 机型分布:启动耗时和设备性能有较强关联,不同小程序或使用场景用户群体的差异可能导致机型分布的差异,进而影响大盘启动耗时。
  • 网络环境:网络环境主要影响网络请求的耗时,如小程序信息获取、代码包下载等。

此外,下列情况也会间接影响启动耗时:

  • 场景/访问来源:不同场景用户访问的页面不同,访问的目的性和自身的等待意愿也有差异,对启动耗时和打开率都有一定影响。
  • 首次访问用户比例:用户首次访问小程序时,需要完整的进行小程序信息准备、代码包下载的流程、代码缓存也需要重新生成,启动耗时会比非首次访问高。
  • 小程序版本更新:小程序版本更新时,用户需要更新小程序信息和代码包,代码缓存也需要重新生成,启动耗时会出现上涨。

关于「性能分析」板块数据

小程序助手中提供了启动过程中和开发者相关的下载耗时、js注入耗时和初次渲染耗时供开发者优化参考。

这里需要注意的是,启动总耗时 ≠ 下载耗时 + js 注入耗时 + 初次渲染耗时。在启动过程中,下载不一定发生,也不一定只下载一个代码包。js 注入耗时和渲染耗时也会受缓存更新而发生波动。

各阶段耗时的下降不一定反应在总耗时的下降。例如小程序新版本发布后,即使各阶段耗时都下降,下载比例的升高反而可能导致总耗时的上升。

3. 影响打开率的因素

启动性能用户的等待意愿是影响打开率的两个关键因素。在部分场景下,如果用户等待意愿较低,即使启动耗时很低,也不一定能获得较高的打开率。