微信官方文档:性能与体验
启动性能
-
代码包体积优化,合理使用分包加载
- 承载更多功能:小程序单个代码包的体积上限为2M,使用分包可以提升小程序代码包总体积上限,承载更多的功能与服务
- 降低代码包下载耗时:使用分包后可以显著减少启动时需要下载的代码包大小,在不影响功能正常使用的前提下,有效降低启动耗时
- 降低小程序代码注入耗时:使用分包可以避免不必要的组件和页面初始化
- 降低内存占用:分包能够实现页面、组件和逻辑较粗粒度的按需加载,从而降低内存的占用
- 分包的扩展功能,独立分包,分包预下载,分包异步化
-
避免非必要的全局自定义组件和插件
如果自定义组件只在某个分包的页面中使用,应定义在页面的配置文件中
如果插件只在某个分包中使用,仅在分包中引用插件 -
控制代码包内的资源文件
小程序代码包在下载时会使用ZSTD算法进行压缩,图片、音频,视频、字体等资源文件会占用较多代码包体积,并且通常难以进一步被压缩,对于下载耗时的影响较大,所以,这类文件尽可能的部署到CDN,并使用URL引入,建议在代码包内的图片只包含一些体积小的图标
及时清理无用代码和资源
代码注入优化(按需注入,用时注入)
启动过程中减少同步API的调用(getSystemInfo、getStorageSync)
避免启动过程进行复杂运算
-
首屏渲染优化
- 使用按需注入和用时注入,减少需要初始化的组件数量
- 启用初始渲染缓存,可以在非首次启动时,提前将页面渲染结果展示给用户
- 避免引用未使用的自定义组件
页面渲染时,会初始化在当前页面配置和全局配置通过usingComponents引用的自定义组件,以及组件所依赖的其他自定义组件,当组件不被使用时,应及时从usingComponents中移除
精简首屏数据
首页渲染的耗时和页面的复杂度正相关,对于复杂页面可以选择进行渐进式的渲染,根据页面内容优先级,对于非关键部分或者不可见的部分进行延迟更新
此外,与视图层渲染无关的数据应尽量不要放在data中,避免影响页面渲染时间提前首屏数据请求
犹豫网络请求需要相对较长的时间,建议在onload或者更早的时机发起网络请求,而不应该等待onready之后
- 数据预拉取:小程序冷启动时,由客户端通过微信后台提前向三方服务器拉取业务数据,减少用户等待时间
- 周期性更新:未打开小程序的情况下,提前从三方服务器拉取数据,减少用户等待时间
- 缓存请求数据,通过本地缓存的数据进行渲染视图
- 使用骨架屏,灰色的区块大致勾勒轮廓,避免展示空白页面,提升用户等待意愿
运行时性能
-
合理使用setData
setData的流程
1)逻辑层虚拟DOM树的遍历和更新,会触发组件生命周期和observer等
2)将data从逻辑层传输到视图层
3)视图层虚拟DOM树的更新、真实DOM元素的更新并触发页面渲染更新-
数据通信
对于第2步,由于小程序的逻辑层和视图层是两个独立的运行环境、分属不同的线程或进程,不能直接进行数据共享,需要进行数据的序列化、跨线程/进程的数据传输、数据的反序列化,因此数据传输过程是异步的、非实时的
使用建议
1)data应只包括渲染相关的数据,setData的方式更新渲染无关的字段,会出发额外的渲染流程,或增加传输的数据量,影响渲染耗时
2)页面或组件的data字段,应用来存放和页面或组件渲染相关的数据(即直接在wxml中出现的字段)
3)页面或组件渲染间接相关的数据可以设置为纯数据字段,key使用setData设置并使用observers监听变化
4)页面或组件渲染无关的数据,应挂在非data的字段下
5)避免在data中包含渲染无关的业务数据
6)避免使用data在页面或组件方法间进行数据共享
7)避免滥用纯数据字段来保存可以使用非data字段保存的数据
8)仅在需要进行页面内容更新时调用setData
9)对连续的setData调用尽可能的进行合并
10)选择合适的setData范围,组件的setData只会引起当前组件和子组件的更新,对于需要频繁更新的页面元素,可以封装为独立组建,在组建内部进行setData,必要时可以使用CSS contain属性限制计算布局、样式和绘制等范围
11)setData应只传发生变化的数据,建议以数据路径形式钢鞭数组中某一项或对象的某个属性,而不是每次更新整个对象
12)页面切后台后的更新操作,应尽量避免,或延迟到页面onShow后延迟进行
13 )控制setData的频率,每次setData都会触发逻辑层虚拟DOM树的遍历和更新,也可能会导致触发一次完整的页面渲染流程。过于频繁(毫秒级)的调用setData,会导致一下后果:
逻辑层JS线程持续繁忙,无法正常响应用户操作的事件,也无法正常完成页面切换
视图层JS线程持续处于忙碌状态,逻辑层->视图层通信耗时上升,视图层收到消息的延时较高,渲染出现明显延迟
视图层无法及时响应用户操作,用户滑动页面时干到明显卡顿,操作反馈延迟,用户操作时间无法及时传递到逻辑层,逻辑层亦无法及时将操作处理结果传递到视图层 -
渲染性能优化
- scroll事件触发频率很高
- 非必要不监听scroll事件,
- 在实现与滚动相关动画时,优先考虑滚动驱动动画(仅<scroll-view>)或wxs响应事件
- 不需要监听事件时,不要保留onPageScroll空函数
- 避免在scroll事件监听函数中执行复杂逻辑
- 避免在scroll事件监听中频繁调用setData或同步API
- 需要监控元素曝光情况,建议使用节点布局相交状态监听IntersectionObserver推断某些节点是否可见,有多大比例可见(避免监听onPageScroll中持续调用selectQuery判断)
- 建议一个页面wxml节点数量少于1000个,节点树深度少于30层,子节点数不大于60个
- 对于比较复杂的数据对象,建议在onload或created时手动赋值到this上,而不是page构造时的参数传入
- 选择高性能的动画实现方式
优先使用CSS渐变、CSS动画、或小程序框架提供的其他动画实现方式完成动画
在一些复杂场景下,如果上述方式不能满足,可以使用wxs响应事件动态调整节点的style属性做到动画效果。
避免通过连续setData实现动画,不可避免,则尽可能改成自定义组件中的setData -
页面切换优化
- 页面切换时,会先调用前一个页面的onHide/onUnload方法,所以避免在onHide/onUnload执行耗时操作
- 页面预加载
-
资源加载优化
- 控制图片资源大小
- 避免滥用image组件的widthFix和heightFix,尽量预先指定图片尺寸,避免加载完成后二次调整尺寸
-
内存优化
- 合理使用分包
- 使用按需注入和用时注入
- 内存分析
- 处理内存告警,必要时使用wx.onMemoryWarning监听内存告警时间,进行必要的内存清理,释放一些暂时不用的组件或者js对象
- 内存泄露
1)小程序长期持有页面实例,导致页面和引用组件无法正常销毁(事件监听持有页面this,页面实例呗页面外变量或者全局变量引用,页面实例呗异步回调长时间引用等)*
2)事件监听未解绑
3)未清理的定时器