最近在做微信小程序的冷启动耗时优化,查阅了一下 小程序官方文档 后发现已经写得非常详尽细致了,这里做一下记录整理,突出开发者重点关注的部分。
启动定义#
以「用户打开小程序」为起点,到小程序「首页渲染完成」为止。
小程序「首页渲染完成」的标志是首个页面 Page.onReady
事件触发。由于启动流程的差异,小程序定义的「首页渲染完成」不等同于浏览器的 DOMContentLoaded
或 load
事件。
启动流程及耗时影响#
从打开微信小程序到首页展示,需要经历如下过程:
各流程不是串行的,会尽可能并行,不能简单分阶段加和计算总启动耗时
资源准备#
小程序相关信息准备
在用户首次访问小程序、小程序版本更新或使用长期未使用的小程序时,微信客户端需要从微信后台获取小程序的头像、昵称、版本、配置、权限等基本信息,以对小程序进行必要的版本管理、权限控制和校验等。
为了在保证信息实时性的前提下,尽量降低对启动耗时的影响,这些信息会在本地缓存,并通过一定的机制进行更新。
小程序版本发布时,会导致启动时需要同步请求的比例上升,进而导致平均启动耗时的上涨。因此,建议开发者 合理规划版本发布。
运行环境准备
小程序的运行环境包括小程序进程、客户端原生部分的系统组件和 UI 元素(如 导航栏、tabBar 等)、渲染页面使用的 WebView
容器、开发者 JavaScript
代码的运行环境、小程序基础库等等。
部分环境(如 JavaScript
引擎、小程序基础库)需要在执行小程序代码之前准备完成,其他的会在启动过程中并行进行。
为了尽可能的降低运行环境准备对启动耗时的影响,微信客户端会根据用户的使用场景和设备资源的使用情况,依照一定策略在小程序启动前对运行环境进行部分地预加载,以降低启动耗时。
代码包准备
在用户首次访问小程序或小程序版本更新时,需要根据用户访问的页面,从微信后台获取代码包地址,从 CDN 下载小程序代码包,并对代码包进行校验。根据小程序页面所在分包和使用的插件不同,一次启动可能需要下载多个代码包或插件包。
除了启动过程,代码包下载在页面跳转、预下载、使用 分包异步化 等过程中也会触发。
为了在保证用户尽可能访问新版本的前提下,尽量降低对启动耗时的影响,小程序代码包会在本地缓存,并通过一定的机制进行更新。
为了降低代码包下载的耗时,微信还采用了 代码包压缩、增量更新、更高效的网络协议、CDN 预连接、代码包复用 等方式
考虑到包大小对用户体验的影响,平台限制单个小程序代码包的大小上限为 2M。为了保证启动速度,开发者应该尽可能的控制启动时用到的代码包大小。具体方法可以参考《代码包体积优化》。
小程序代码注入(逻辑层)#
小程序启动时需要从代码包内读取小程序的配置和代码,并注入到 JavaScript
引擎中。在主包代码注入过程中,会触发小程序的 App.onLaunch
和 App.onShow
生命周期。如果小程序使用了插件或扩展库,在注入开发者代码之前,还会先注入对应插件和扩展库的代码。
在部分平台上,微信客户端会使用 V8 引擎的 Code Caching 技术对代码编译结果进行缓存,降低非首次注入时的编译耗时。
由于「首页渲染」需要使用逻辑层发送的数据,如果小程序代码注入耗时过长,会延迟「首页渲染」开始的时间。建议开发者参考《代码注入优化》章节进行优化。
小程序代码注入(视图层)#
开发者的 WXSS
和 WXML
会编译成 JavaScript
代码注入到视图层,包含页面渲染需要的页面结构和样式信息。
视图层和逻辑层的小程序代码注入是并行进行的。
由于「首页渲染」需要使用视图层的页面结构和样式信息,如果小程序代码注入耗时过长,会影响渲染数据从逻辑层到达视图层的时间,影响「首页渲染」的耗时。
虽然开发者不能直接修改视图层生成的 JS 代码,但是可以通过使用「按需注入」、移除未使用的自定义组件等方式降低这部分耗时。
首页(初次)渲染#
在逻辑层小程序代码注入完成后,小程序框架会根据用户访问的页面,进行页面组件树初始化,生成初始数据发送到视图层,并依次触发首页的 Page.onLoad
, Page.onShow
生命周期。
在完成视图层代码注入,并收到逻辑层发送的初始数据后,结合从初始数据和视图层得到的页面结构和样式信息,小程序框架会进行小程序首页的渲染,展示小程序首屏,并触发首页的 Page.onReady
事件,标志小程序启动过程完成。
首页渲染耗时是启动过程的最后一环,直接影响小程序的启动耗时。耗时长短与页面结构复杂度、参与渲染的自定义组件数量有关。建议开发者参考《首屏渲染优化》章节进行优化。
如果启用了「按需注入」,部分组件代码注入会被延迟到本阶段执行,导致阶段耗时上涨,但总耗时一般会下降。
首屏内容展示#
「首页渲染」完成后,小程序启动流程完成,Loading 消失,此时一般情况下用户应该能立刻看到首屏内容。如依赖网络请求则需要等待 setData 进行页面更新后才会展示,可先展示「骨架屏」来避免白屏,以优化用户体验。
异步 setData 触发绘制的首屏内容展示不一定会计入启动耗时统计,但是会延迟用户看到页面内容的时间,影响用户体验。建议开发者参考《首屏渲染优化》章节进行优化。
优化策略#
在小程序启动流程中,代码包准备、小程序代码注入和首页渲染的耗时是与小程序本身相关的,开发者可以从以下方面进行优化工作:
- 代码包体积优化
- 分包加载、独立分包、分包异步化
- 普通分包加载注意不能相互引用,可使用分包异步化解决
- 独立分包不需要下载主包,即也不能依赖主包
- 避免非必要的全局自定义组件和插件
- 控制代码包内的资源文件:图片使用 CDN 引入
- 及时清理无用代码和资源:结合开发者工具提供的 代码静态依赖分析 使用
- 分包加载、独立分包、分包异步化
- 代码注入优化
- 使用按需注入和用时注入:结合 避免非必要的全局自定义组件和插件 使用
- 启动时减少同步 API 调用:在小程序初始化代码(Page,App 定义之外的内容)和
App.onLaunch, App.onShow, Page.onLoad, Page.onShow
几个生命周期中,避免阻塞当前 JS 线程 - 启动时减少复杂运算:同上
- 首屏渲染优化
- 启用按需注入和用时注入:减少需要初始化的组件数量
- 启用初始渲染缓存:二次启动跳过逻辑层初始化过程
- 避免引用未使用的自定义组件
- 精简首屏数据:与视图层渲染无关的数据尽量不要放在
data
中 - 提前首屏数据请求:在
Page.onLoad
或更早的时机发起网络请求 - 本地缓存请求数据:
wx.setStorage
、wx.getStorage
- 骨架屏
- 其他优化建议
- 控制版本发布频率