其他启动性能优化建议

除了 代码包体积、代码注入、首屏渲染之外,发版频率等因素也会影响微信小程序启动耗时。

针对这些因素,我们建议开发者:

1. 合理规划版本发布

微信小程序启动时如果检测到版本更新(具体策略请参考微信小程序更新机制),会进行以下操作,影响启动耗时

  • 重新获取微信小程序的基础信息
  • 进行微信小程序代码包的增量更新
  • 重新生成 JS 代码的 Code Cache
  • 重新生成初始渲染缓存

能够快速迭代发布是微信小程序相对 APP 的一个优势,但是过于频繁的新版本发布可能会导致部分用户每次使用都需要进行微信小程序的更新,导致平均启动耗时变长。

在不影响微信小程序正常功能迭代的前提下,我们建议开发者提前做好版本规划,控制版本发布的频率。

回退和发布新版本对于启动耗时的影响是一致的。

首屏渲染优化

页面首屏渲染的优化,目的是让「首页渲染完成」(Page.onReady) 尽可能提前。但很多情况下「首页渲染完成」可能还是空白页面,因此更重要的是让用户能够更早地看到页面内容(First Paint 或 First Contentful Paint)。

1. 使用「按需注入」和「用时注入」

除了优化代码注入的耗时外,「按需注入」和「用时注入」也可以减少需要初始化的组件数量,降低实际页面渲染的耗时,使「首页渲染完成」提前。

启用「按需注入」之后,部分组件代码注入会被延迟到首页渲染阶段执行,导致阶段耗时上涨,但总耗时一般会下降。

2. 启用「初始渲染缓存」

自基础库版本 2.11.1 起,微信小程序支持启用初始渲染缓存。开启后,可以在非首次启动时,使视图层不需要等待逻辑层初始化完毕,而直接提前将页面渲染结果展示给用户,这可以使「首页渲染完成」和页面对用户可见的时间大大提前。

3. 避免引用未使用的自定义组件

在页面渲染时,会初始化在当前页面配置和全局配置通过 usingComponents 引用的自定义组件,以及组件所依赖的其他自定义组件。未使用的自定义组件会影响渲染耗时。

当组件不被使用时,应及时从 usingComponents 中移除。

4. 精简首屏数据

首页渲染的耗时与页面的复杂程度正相关。对于复杂页面,可以选择进行渐进式的渲染,根据页面内容优先级,优先展示页面的关键部分,对于非关键部分或者不可见的部分可以延迟更新。

此外,与视图层渲染无关的数据应尽量不要放在 data 中,避免影响页面渲染时间。

5. 提前首屏数据请求

很多微信小程序在渲染首页时,需要依赖服务端的接口数据(如商品列表等),此时微信小程序的首页可能是空白或者骨架屏。

由于网络请求需要相对较长的时间,我们建议开发者在 Page.onLoad 或更早的时机发起网络请求,而不应等待 Page.onReady 之后再进行。

为了进一步提前请求发起的时机,微信小程序为开发者提供了以下能力:

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

6. 缓存请求数据

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

7. 骨架屏

骨架屏通常用于在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。

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

开发者工具提供了生成骨架屏的能力,帮助开发者更便捷地维护骨架屏。

代码注入优化

微信小程序代码注入的优化可以从优化代码量优化执行耗时两个角度着手。

1. 使用按需注入

推荐所有微信小程序使用

通常情况下,在微信小程序启动时,启动页面依赖的所有代码包(主包、分包、插件包、扩展库等)的所有 JS 代码会全部合并注入,包括其他未访问的页面以及未用到自定义组件,同时所有页面和自定义组件的 JS 代码会被立刻执行。这造成很多没有使用的代码在微信小程序运行环境中注入执行,影响注入耗时和内存占用。

自基础库版本 2.11.1 起,可以通过开启「按需注入」特性避免不必要的代码注入和执行,以降低微信小程序的启动时间和运行时内存。

{
  "lazyCodeLoading": "requiredComponents"
}

注意:启用按需注入后,页面 JSON 配置中定义的所有组件和 app.jsonusingComponents 配置的全局自定义组件,都会被视为页面的依赖并进行注入和加载。建议开发者及时移除 JSON 中未使用自定义组件的声明,并尽量避免在全局声明使用率低的自定义组件,否则可能会影响按需注入的效果。

2. 使用用时注入

在打开上述「按需注入」特性的前提下,可以通过「用时注入」特性使一部分自定义组件不在启动时注入,而是在真正被渲染时才进行注入,进一步降低微信小程序的启动和首屏时间。

3. 启动过程中减少同步 API 的调用

微信小程序启动流程中,会注入开发者代码并顺序同步执行 App.onLaunch, App.onShow, Page.onLoad, Page.onShow

在微信小程序初始化代码(Page,App 定义之外的内容)和上述启动相关的几个生命周期中,应尽量减少或不调用同步 API。绝大多数同步 API 会以 Sync 结尾,但有部分特例,比如 getSystemInfo

同步 API 虽然使用起来更简单,但是会阻塞当前 JS 线程,影响代码执行。如非必要,应尽可能的使用异步 API 代替同步,并将启动过程中非必要的同步 API 调用延迟到启动完成后进行。

常见的开发者容易在启动时过于频繁调用的 API 有:

3.1 getSystemInfo/getSystemInfoSync

由于历史原因,这两个接口都是同步实现。由于 getSystemInfo 接口里承载了过多内容,单次调用可能比较久。

如非必要,建议开发者对调用结果进行缓存,避免重复调用。启动过程中应尽可能最多调用一次

建议优先使用拆分后的 getSystemSetting/getAppAuthorizeSetting/getDeviceInfo/getWindowInfo/getAppBaseInfo 按需获取信息,或使用使用异步版本 getSystemInfoAsync。

3.2 getStorageSync/setStorageSync

getStorageSync/setStorageSync 应只用来进行数据的持久化存储,不应用于运行时的数据传递或全局状态管理。启动过程中过多的读写存储,也会显著影响微信小程序代码注入的耗时。

对于简单的数据共享,可以使用在 App 上增加全局数据对象完成:

// app.js
App({
  globalData: { // 全局共享的数据
    userName: 'Wechat'
  }
})

// pages/index.js
const app = getApp()
Page({
  onLoad() {
    const { userName } = app.globalData
  }
})

4. 避免启动过程进行复杂运算

在微信小程序初始化代码(Page,App 定义之外的内容)和启动相关的几个生命周期中,应避免执行复杂的运算逻辑。复杂运算也会阻塞当前 JS 线程,影响启动耗时。建议将复杂的运算延迟到启动完成后进行。

代码包体积优化

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

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

1. 合理使用分包加载

推荐所有微信小程序使用

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

分包加载具有以下优势:

  • 承载更多功能:微信小程序单个代码包的体积上限为 2M,使用分包可以提升微信小程序代码包总体积上限,承载更多的功能与服务。
  • 降低代码包下载耗时:使用分包后可以显著减少启动时需要下载的代码包大小,在不影响功能正常使用的前提下,有效降低启动耗时。
  • 降低微信小程序代码注入耗时:若未开启按需注入,微信小程序编译时会将所有 js 文件打包成同一个文件一次性的注入,并执行所有页面和自定义组件的代码。分包后可以降低注入和实际执行的代码量,从而降低注入耗时。
  • 降低页面渲染耗时:使用分包可以避免不必要的组件和页面初始化。
  • 降低内存占用:分包能够实现页面、组件和逻辑较粗粒度的按需加载,从而降低内存的占用。

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

1.1 独立分包

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

1.2 分包预下载

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

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

1.3 分包异步化

「分包异步化」将微信小程序的分包从页面粒度细化到组件甚至文件粒度。这使得本来只能放在主包内页面的部分插件、组件和代码逻辑可以剥离到分包中,并在运行时异步加载,从而进一步降低启动所需的包大小和代码量。

分包异步化能有效解决主包大小过度膨胀的问题。

2. 避免非必要的全局自定义组件和插件

app.json 中通过 usingComponents 全局引用的自定义组件和通过 plugins 全局引入的插件,会在微信小程序启动时随主包一起下载和注入 JS 代码,影响启动耗时。

即使扩展库和部分官方插件不占用主包大小,但是启动时仍然需要下载和注入 JS 代码,对启动耗时的影响和其他插件并没有区别。

  • 如果自定义组件只在某个分包的页面中使用,应定义在页面的配置文件中
    • 全局引入的自定义组件会被认为是所有分包、所有页面都需要的,会影响「按需注入」的效果和微信小程序代码注入的耗时。
  • 如果插件只在某个分包的中使用,请仅在分包中引用插件
    • 例如:很多微信小程序会用到「微信小程序直播」插件,但是直播功能通常不在主包页面中使用或较为低频,此时建议通过分包引入「微信小程序直播」插件。
    • 如果确实需要在主包中或被多个分包使用的插件,仍可以考虑将插件置于一个分包,并通过「分包异步化」的形式异步引入

微信小程序启动流程介绍

在进行启动优化之前,我们先介绍一下微信小程序的启动过程。了解微信小程序的启动流程,可以帮助开发者更有针对性地选择性能优化的手段,分析性能优化的效果。

本文的启动流程以安卓和 iOS 为准,其他平台可能会略有差异。

注:微信小程序启动的各流程不是串行的,会尽可能的并行。计算总启动耗时不能简单的分阶段加和。

下列图片简要描述了部分情况下的微信小程序启动流程(注意:其中矩形块的宽度不与对应阶段耗时成比例)。

微信小程序启动流程示意图

开发者可以通过wx.getPerformance接口中 entryType 为 navigation,name 为 appLaunch 的指标(PerformanceEntry),获取页面切换耗时时。

微信小程序启动过程主要包括以下几个环节:

1. 资源准备

1.1 运行环境准备

微信小程序的运行环境包括微信小程序进程、客户端原生部分的系统组件和 UI 元素(如 导航栏、tabBar 等)、渲染页面使用的 WebView 容器、开发者 JavaScript 代码的运行环境、微信小程序基础库等等。

部分环境(如 JavaScript 引擎、微信小程序基础库)需要在执行微信小程序代码之前准备完成,其他的会在启动过程中并行进行。运行环境的准备时间相对较长(尤其是在低端设备上),会对微信小程序启动产生严重影响。

环境预加载

为了尽可能的降低运行环境准备对启动耗时的影响,微信客户端会根据用户的使用场景和设备资源的使用情况,依照一定策略在微信小程序启动前对运行环境进行部分地预加载,以降低启动耗时。

我们希望微信小程序启动时尽可能都使用到预加载的环境,但由于受到访问场景、设备资源状况和操作系统调度的影响,并不能保证每次微信小程序启动时都可以命中预加载的环境

对启动耗时的影响

运行环境准备耗时较长,如果启动时没有命中预加载的环境,对微信小程序的启动耗时会有明显影响。耗时长短与平台、设备性能、预加载比例有关。

  • 由于系统功能和启动流程实现的差异,通常安卓系统运行环境准备耗时要远高于 iOS。
  • 低端机系统资源比较紧张,预加载的环境会更容易被系统清理,导致预加载比例偏低。
  • 预加载比例越高,平均启动耗时一般可以越低。

这部分逻辑完全由微信客户端控制,开发者目前无法直接进行优化。

1.2 微信小程序相关信息准备

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

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

信息的获取和更新需要发起网络请求。请求分为两种情况:

(1) 同步请求:会阻塞微信小程序的启动流程,影响微信小程序的启动耗时。有以下情况需要进行同步请求:

  • 首次访问:用户首次访问该微信小程序(或微信小程序被清理)时,客户端没有缓存,需要同步请求微信小程序相关信息。
  • 同步更新:微信会在后台定期检查经常使用的微信小程序是否更新。如果启动时已知微信小程序有新版本,会同步更新信息。
  • 强制更新:用户长时间未使用微信小程序时,为保障信息的实时性,会强制同步更新信息。

(2) 异步请求:与启动流程并行,不影响启动耗时。主要发生在:

  • 异步更新:已使用过的微信小程序,定期检查暂未发现微信小程序有新版本,则优先使用本地缓存的信息完成启动,并异步进行更新。

对启动耗时的影响

在用户首次访问微信小程序、微信小程序版本更新或使用长期未使用的微信小程序时,信息的获取和更新会影响微信小程序的启动耗时,耗时长短主要与网络环境有关。

从大盘来看,微信小程序版本发布时,会导致启动时需要同步请求的比例上升,进而导致平均启动耗时的上涨。因此,建议开发者合理规划版本发布。

这部分逻辑完全由微信客户端控制,开发者目前无法直接进行优化。

1.3 代码包准备

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

除了启动过程,代码包下载在页面跳转、预下载、使用分包异步化等过程中也会触发。

为了在保证用户尽可能访问新版本的前提下,尽量降低对启动耗时的影响,微信小程序代码包会在本地缓存,并通过更新机制进行更新。

和相关信息准备类似,代码包下载也会有同步和异步两种情况:

(1) 同步下载:会阻塞微信小程序的启动流程,影响微信小程序的启动耗时。有以下情况需要进行同步下载:

  • 首次下载:用户首次访问该微信小程序(或微信小程序被清理)时,客户端没有缓存,需要同步下载代码包。
  • 同步更新:对于微信小程序信息发生「同步更新」或「强制更新」的情况,如果检测到微信小程序版本更新,会同步下载代码包。

(2) 异步下载:与启动流程并行,不影响启动耗时。主要发生在:

  • 异步更新:对于微信小程序信息发生「异步更新」的情况,如果检测到微信小程序版本更新,会异步更新代码包。

为了降低代码包下载的耗时,我们采用了包括但不限于以下方式:

  • 代码包压缩:采用 Zstandard 算法对微信小程序代码包进行压缩,以尽可能降低下载过程中传输的数据量。
  • 增量更新:当代码包发生更新,不需要重新下载完整的代码包,只需要下载根据算法生成的体积很小的增量包进行更新。
  • 更高效的网络协议:下载代码包优先使用 QUIC 和 HTTP/2。
  • 预先建立连接:在下载发生前,提前和 CDN 建立连接,降低下载过程中 DNS 请求和连接建立的耗时。
  • 代码包复用:对每个代码包都会计算 MD5 签名。即使发生了版本更新,如果代码包的 MD5 没有发生变化,则不需要重新进行下载。

对启动耗时的影响

下载耗时是启动耗时中的重要瓶颈,在用户首次访问微信小程序或微信小程序版本更新时,代码包的下载会对启动耗时造成影响。耗时长短与网络环境,代码包压缩后大小,以及是否命中增量更新有关。

考虑到包大小对用户体验的影响,平台限制单个微信小程序代码包的大小上限为 2M。代码包上限的增加,对于开发者来说能够实现更丰富的功能,但对于用户来说也增加了流量和本地空间的占用。为了保证启动速度,开发者应该尽可能的控制启动时用到的代码包大小。具体方法可以参考《代码包体积优化》。

2. 微信小程序代码注入(逻辑层)

微信小程序启动时需要从代码包内读取微信小程序的配置和代码,并注入到 JavaScript 引擎中。在主包代码注入过程中,会触发微信小程序的 App.onLaunchApp.onShow 生命周期。如果微信小程序使用了插件或扩展库,在注入开发者代码之前,还会先注入对应插件和扩展库的代码。

为了降低微信小程序代码注入的耗时,我们采用了包括但不限于以下方式:

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

注意:如果代码中使用了 use asm,会导致 V8 的 Code Caching 失效。

对启动耗时的影响

微信小程序代码的注入耗时直接影响微信小程序的启动耗时。耗时长短与代码复杂度、同步接口调用和一些复杂的计算有关。如果未启用「按需注入」,耗时还会与启动使用到分包内的页面和自定义组件总数有关

由于「首页渲染」需要使用逻辑层发送的数据,如果微信小程序代码注入耗时过长,会延迟「首页渲染」开始的时间。建议开发者参考《代码注入优化》章节进行优化。

3. 微信小程序代码注入(视图层)

开发者的 WXSS 和 WXML 会编译成 JavaScript 代码注入到视图层,包含页面渲染需要的页面结构和样式信息。

我们采用和「微信小程序代码注入(逻辑层)」相似的方式优化注入耗时。

视图层和逻辑层的微信小程序代码注入是并行进行的

对启动耗时的影响

微信小程序代码的注入耗时直接影响微信小程序的启动耗时。耗时长短与当前页面结构复杂度和页面使用的自定义组件数量有关。如果未启用「按需注入」,耗时还会与启动使用到分包内的页面和自定义组件总数有关

由于「首页渲染」需要使用视图层的页面结构和样式信息,如果微信小程序代码注入耗时过长,会影响渲染数据从逻辑层到达视图层的时间,影响「首页渲染」的耗时。

虽然开发者不能直接修改视图层生成的 JS 代码,但是可以通过使用「按需注入」、移除未使用的自定义组件等方式降低这部分耗时。

4. 首页(初次)渲染

在逻辑层微信小程序代码注入完成后,微信小程序框架会根据用户访问的页面,进行页面组件树初始化,生成首屏渲染相关数据发送到视图层,并依次触发首页的 Page.onLoad, Page.onShow 生命周期。

首屏渲染相关数据包括 Page 初始化参数中 data 属性值,和部分比较早发出的 setData 数据(哪些 setData 可以计入首页渲染与渲染层和逻辑层之间的初始化时序相关,目前没有可以保证一定能够计入的情况)。

在完成视图层代码注入,并收到逻辑层发送的首屏渲染相关数据后,结合从初始数据和视图层得到的页面结构和样式信息,微信小程序框架会进行微信小程序首页的渲染,展示微信小程序首屏,并触发首页的 Page.onReady 事件。

如果开启了「初始渲染缓存」,「首页渲染」可以直接使用缓存完成,不依赖逻辑层的初始数据,降低启动耗时。

微信小程序框架层面,以 Page.onReady 事件触发标志微信小程序启动过程完成

对启动耗时的影响

首页渲染耗时是启动过程的最后一环,直接影响微信小程序的启动耗时。耗时长短与页面结构复杂度、参与渲染的自定义组件数量有关。建议开发者参考《首屏渲染优化》章节进行优化。

如果启用了「按需注入」,部分组件代码注入会被延迟到本阶段执行,导致阶段耗时上涨,但总耗时一般会下降。

5. 首屏内容展示

「首页渲染」完成后,微信小程序启动流程完成,Loading 消失,此时一般情况下用户应该能立刻看到首屏内容。

但是如果首页的主体内容依赖网络请求(例如 wx.request)等异步来源,用户并不一定能立刻看到有意义的完整界面,可能看到的仍然是白屏界面。需要等待网络请求异步返回后,调用 setData 进行页面更新,才能呈现真正的页面。

通常情况下,开发者也会选择先展示「骨架屏」来避免白屏,以优化用户体验。

对启动耗时的影响

异步 setData 触发绘制的首屏内容展示不一定会计入启动耗时统计,但是会延迟用户看到页面内容的时间,影响用户体验。建议开发者参考《首屏渲染优化》章节进行优化。

常见问题

(1) 为什么「开发版」和「体验版」微信小程序启动比「正式版」慢一些?

「开发版」和「体验版」微信小程序的启动流程和代码包下载链路会和「正式版」有所差别,也会有更严格的权限控制,因此启动耗时要慢于「正式版」微信小程序。

对于「开发版」微信小程序,为了方便开发调试,基础库会启用很多调试相关的能力,例如 vConsole、sourceMap 等,日志输出的等级也会更低,因此启动耗时和页面切换耗时也会有一定延长。

(2) 为什么安卓和 iOS 的启动耗时差异那么大?

两个平台的设备性能、系统功能和启动流程实现存在一定差异:

  • iOS 设备的平均性能要好于安卓;
  • iOS 微信小程序和微信共用进程,而 Android 上微信小程序运行在独立进程,需要额外的进程创建和一些基础模块的初始化流程;
  • iOS 上需要使用系统提供的 WebView 和 JavaScript Core,初始化开销几乎可以忽略;
  • 安卓 UI 和系统组件的创建的开销远高于 iOS。

启动性能

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

本章节的「启动」特指微信小程序冷启动,不包括微信小程序后台切前台的热启动。关于冷/热启动的定义,请参考微信小程序运行机制

1. 微信小程序启动的定义

微信小程序的启动过程以「用户打开微信小程序」为起点,到微信小程序「首页渲染完成」为止

「用户打开微信小程序」可能是由用户点击访问触发,也可能通过扫码、微信小程序跳微信小程序或 APP 打开微信小程序等入口触发。从扫码、APP 等场景打开微信小程序时,可能会有前置的跳转和校验流程,不包含在微信小程序启动流程的讨论范围之内。

微信小程序「首页渲染完成」的标志是首个页面 Page.onReady 事件触发。由于启动流程的差异,微信小程序定义的「首页渲染完成」不等同于浏览器的 DOMContentLoadedload 事件。

要了解微信小程序启动的具体流程,请参考《微信小程序启动流程》章节的介绍。

2. 打开率/到达率

微信小程序「首页渲染完成」次数与「微信小程序启动」次数的比值也被称为(PV)打开率或(PV)到达率。与之对应的 流失率 = 1 - 打开率

打开率受到下列因素影响:

  • 启动性能:启动耗时越长,白屏时间越久,用户越可能因为失去耐心而退出微信小程序,打开率也会越低;
  • 用户等待意愿:用户等待意愿越强,等待时间也会更久,在启动耗时一致的情况下,打开率也会越高。用户等待意愿与使用微信小程序的场景有关,例如:
    • 扫码、搜索等用户目的性较强的场景,通常等待意愿也更强;
    • 广告类的场景下,用户等待意愿较低,要获得较高的打开率,启动性能优化会更加有必要。