跳到主要内容

如何提升小程序性能

小程序性能优化的具体维度 微信 IDE 对小程序性能进行评分有以下几个维度:

  • 避免过大的 WXML 节点数目

  • 避免执行脚本的耗时过长的情况

  • 避免首屏时间太长的情况

  • 避免渲染界面的耗时过长的情况

  • 对网络请求做必要的缓存以避免多余的请求

  • 所有请求的耗时不应太久

  • 避免 setData 的调用过于频繁

  • 避免 setData 的数据过大

  • 避免短时间内发起太多的图片请求

  • 避免短时间内发起太多的请求

其实这 10 个性能的评分标准并不仅仅适用于微信小程序,有几条(2~6、9和10)是前端开发领域的通用性能指标,而且如果你用过 Vue/React 之类的MVVM 框架,以上指标可以全部应用到基于 Vue/React 框架开发的 Web 应用程序中。接下来我们一条条地剖析这几个指标对于性能优化的意义以及对应的解决方案。

当然了,有些条目的优化方向是一致的(比如 7 和 8 是为了提高渲染性能),在剖析过程中我们会进行必要的聚合,这样更利于从全局的角度了解这些性能评分标准之间的关联。

小程序首屏加载优化​

  • 合并请求:
    • 首页请求了较多的接口,可以考虑合并请求
  • 分逻辑调用接口:
    • 部分接口可以分逻辑调用,可以根据具体场景进行逻辑判断,并非所有场景下都需要调用接口
  • 资源预加载:
    • 预加载首页所需的静态资源,如图片、样式等,可以提前加载这些资源,减少页面加载时的等待时间,提升用户体验
  • 按需加载、延迟加载:
    • 比如首屏渲染的时候,图片加载会占有一定的时间,可以将图片通过滚动加载、只加载可视区域部分,而不是一次性加载

小程序包体积优化​

  • 针对wxss样式,可以自己实现一个脚本,把无用的dead code给去掉,小程序不同与h5,小程序对包体积要求比较极致,既然样式没有使用到,就不应该存在代码中

  • 组件分包异步化加载

    • 对于较大的组件库或功能模块,可以考虑将其打包为独立的分包,并在需要时异步加载
  • 尽可能少兼容老版本浏览器

    • 在package.json,可以调整browserlist的配置,调整之后,可以减少大量无用的css兼容语法

setData优化​

  • setData不能一次性传太多数据,如果列表太长,可以分开渲染
  • setData调用过于频繁
  • 跟页面无关的数据不要保存在data中,不要用setData更新,因为setData会触发页面渲染
  • setData支持颗粒更新,可以指定到具体的属性值更新
javascript
setData({
[`goodsList[${categoryIndex}][${goodsIndex}]`]:goodsItem
})

为什么setData不能传太多数据​

由setData的底层实现可知,我们的数据传输实际是⼀次 evaluateJavascript 脚本过程,当数据量过大时会增加脚本的编译执行时间,占用 WebView JS 线程

当数据量过大的时候,会增加脚本和编译执行时间,占用webview线程和JsCore线程

为什么setData不能频繁调用​

在我们分析过的⼀些案例里,部分小程序会非常频繁(毫秒级)的去setData,其导致了两个后果: Android 下⽤户在滑动时会感觉到卡顿,操作反馈延迟严重,因为 JS 线程⼀直在编译执行渲染,未能 及时将用户操作事件传递到逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层; 渲染有出 现延时,由于 WebView 的 JS 线程⼀直处于忙碌状态,逻辑层到页面层的通信耗时上升,视图层收到 的数据消息时距离发出时间已经过去了⼏百毫秒,渲染的结果并不实时;

简单来说,每次setData都要经过逻辑层和渲染层,逻辑层和渲染层是不同进程。频繁调用每次经过2个进程会影响性能和用户体验

图片优化​

  • 可以使用CDN+OSS
  • 雪碧图
  • svg
  • 懒加载

内存优化​

官网文档

小程序启动的过程中,除了代码包下载以外,代码注入也是一个主要的耗时环节,如果只是分包加载,还有以下问题:

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

如何内存分析?​

如果要更精细的分析小程序逻辑层的内存分布情况,可以使用开发者工具调试器的「内存调试」或「真机调试 2.0」提供的「内存调试」能力

合理使用分包​

使用分包加载不仅能优化启动耗时,也能够实现页面、组件和逻辑较粗粒度的按需加载,从而降低内存的占用

使用按需注入和用时注入​

官网文档

通过开启「按需注入」和「用时注入」,可以在运行时避免加载未使用到的页面和组件,降低运行时的内存占用

按需注入​

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

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

json
//app.json配置
{
"lazyCodeLoading": "requiredComponents"
}

启用按需注入后:

  • 小程序仅注入当前访问页面所需的自定义组件和页面代码。未访问的页面、当前页面未声明的自定义组件不会被加载和初始化,对应代码文件将不被执行
  • 页面 JSON 配置中定义的所有组件和 app.json 中 usingComponents 配置的全局自定义组件,都会被视为页面的依赖并进行注入和加载 【建议开发者:尽量避免在全局声明使用率低的自定义组件,否则可能会影响按需注入的效果。】

优缺点:

  • 按需注入可以在分包加载的基础上进一步提升小程序启动时间;但是对代码文件依赖管理的要求比较高,如果有些全局方法和属性不是在当前页面引用的代码文件中声明赋值的,则在当前页面无法正常调用,增加了逻辑复杂性。

用时注入​

在开启「按需注入」特性的前提下,「用时注入」可以指定一部分自定义组件不在小程序启动时注入,而是在真正渲染的时候才进行注入

在已经指定 lazyCodeLoading 为 requiredComponents 的情况下,为自定义组件配置 占位组件,组件就会自动被视为用时注入组件:

  • 每个页面内,第一次渲染该组件前,该组件都不会被注入;
  • 每个页面内,第一次渲染该组件时,该组件会被渲染为其对应的占位组件,渲染流程结束后开始注入;
  • 注入结束后,占位组件被替换回对应组件。

处理内存告警​

当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。在 iOS 上,当微信客户端在一定时间间隔内连续收到系统内存告警时,会根据一定的策略,主动销毁小程序,并提示用户 [运行内存不足,请重新打开该小程序 ]

  • 可以在小程序在必要时使用 wx.onMemoryWarning 监听内存告警事件,进行必要的内存清理。例如:释放一些暂时不用的组件或 JS 对象。

小程序常见的内存泄露​

存在内存泄露问题会导致小程序在运行过程中内存占用持续增长,引起小程序闪退或被被微信强制销毁

使用Worker​

在小程序中使用Worker可以显著提高性能,特别是在处理复杂计算、耗时操作和IO密集型任务时

Worker是一个独立的线程,它运行在后台,并且与主线程(渲染线程)分开,这样可以避免主线程被阻塞,从而提高小程序的响应速度和性能。

基本使用:​

复杂的计算逻辑耗时 交给Worker线程处理

在 app.json定义workers字段来存放workers的目录,如果是uniapp/taro在manifest.json

  • 1、创建Worker实例
  • 2、向Worker发送消息
  • 3、Worker计算完数据之后,将结果发送给主线程
  • 4、主线程监听Worker返回的消息

发送消息通过postMessage,监听消息通过onMessage

js
// worker/index.js

// 监听主线程发送的消息
worker.onMessage(function (res) {
console.log(res)
})

主包如何优化​

如果主包体积已经快接近2M, 并且分包也快到了20M, 这时候如何优化?

  • 1、可以将小程序的一些主包的页面,某些组件页面,通过H5来内嵌,这样可以减少包的体积

  • 2、分析第三方库的依赖来分析包的体积,提取公共模块,减少包体积

  • 3、css代码优化

    • 合并重复的样式
    • css压缩
  • 4、页面的逻辑代码,有相同逻辑的,可以提取出来放在公共代码里面

主包有哪些内容?

  • 主包:一般只包含项目的启动页面或TbbBar页面

    • tabbar页面必须是主包
    • 一些分包需要用到的公共资源(样式、js、图片)可以放在主包
      • 因为分包可以使用主包的东西