微信小程序优化思路
这里的小程序特指微信小程序
指标
- FCP:白屏加载结束;
- FMP:首屏渲染完成;
- TTI:所有内容加载完成;
官方性能指标
主要围绕 渲染表现、setData 数据量、元素节点数 和 网络请求延时 这几个维度来给予定义;
- 首屏时间不超过 5 秒;
- 渲染时间不超过 500ms;
- 每秒调用
setData
的次数不超过 20 次; setData
的数据在JSON.stringify
后不超过 256kb;- 页面 WXML 节点少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个;
- 所有网络请求都在 1 秒内返回结果;
微信小程序性能指标详解
性能分析工具
小程序底层架构
不同于传统浏览器单线程,小程序采用双线程模型。
- 视图层:Webview 线程,启用不同 webview 来渲染不同的小程序页面
- 逻辑层:单独的线程执行 JS 代码,可以控制视图层的逻辑
优势:不会经常出现阻塞情况
任何线程间的数据传输都是有延时的,意味着逻辑层和视图层间通信是异步行为 **
启动慢
启动过程:
- 准备运行环境:启动双线程环境,并在线程中完成小程序基础库的初始化和预执行
- 下载小程序代码包:下载编译后的代码包到本地,有缓存优先读取缓存
- 加载小程序代码包:基础库会完成所有页面的注册
- 初始化小程序首页
优化:
- 无用文件,函数,样式剔除
- 减少代码包中静态资源文件:图片启用CDN
- 逻辑后移,精简前端逻辑:不涉及前端计算展示类逻辑,都可后移
- 复用模板插件
- 分包加载
- 部分页面 H5 化
白屏时间长
影响因数:
- 网络资源加载时间
- 渲染时间
优化:
- 启用本地缓存
- 数据预拉取
- 跳转时预拉取
- 分包预下载
- 非关键渲染数据延迟请求
- 分屏渲染
- 接口聚合,请求合并
- 骨架屏
- 图片资源优化
- 使用 WebP 格式
- 图片裁剪&降质
- 图片懒加载,雪碧图优化
- 降级加载大图资源:我们可以先呈现高度压缩的模糊图片,同时利用一个隐藏的 节点来加载原图,待原图加载完成后再转移到真实节点上渲染
注意,具有 display: none 样式的 标签只会加载图片资源,但不渲染。
javascript
<!-- banner.wxml -->
<image src="{{url}}" />
<!-- 图片加载器 -->
<image
style="width:0;height:0;display:none"
src="{{preloadUrl}}"
bindload="onImgLoad"
binderror="onErrorLoad"
/>
javascript
// banner.js
Component({
ready() {
this.originUrl = 'https://path/to/picture' // 图片源地址
this.setData({
url: compress(this.originUrl) // 加载压缩降质的图片
preloadUrl: this.originUrl // 预加载原图
})
},
methods: {
onImgLoad() {
this.setData({
url: this.originUrl // 加载原图
})
}
}
})
渲染性能
跳转页面过程:
- 准备新的 webview 线程环境,包括基础库的初始化;
- 从逻辑层到视图层的初始数据通信;
- 视图层根据逻辑层的数据,结合 WXML 片段构建出节点树(包括节点属性、事件绑定等信息),最终与 WXSS 结合完成页面渲染;
由于微信会提前开始准备 webview 线程环境,所以小程序的渲染损耗主要在后两者 数据通信 和 节点树创建/更新 的流程中。相对应的,比较有效的渲染性能优化方向就是:
- 降低线程间通信频次
- 减少线程间通信的数据量
- 减少 WXML 节点数量
优化:
- 合并
setData
调用 - 只把渲染相关的数据放在
data
中 - 应用层的数据 diff
- 组件层的 diff
- 去掉不必要的事件绑定
- 去掉不必要的节点属性
- 适当的组件颗粒度
- 事件总线,替代组件间数据绑定的通信方式:eventBus
javascript
// 全局事件调度
class EventBus {
constructor() {
this.events = {}
}
on(key, cb) { this.events[key].push(cb) }
trigger(key, args) {
this.events[key].forEach(function (cb) {
cb.call(this, ...args)
})
}
remove() {}
}
const event = new EventBus()
// 事件订阅者 子组件
Component({
created() {
event.on('data-ready', (data) => { this.setData({ data }) })
}
})
// 事件发布着 Parent
Component({
ready() {
event.trigger('data-ready', data)
}
})
内存高
当小程序占用系统资源过高,就有可能会被系统销毁或被微信客户端主动回收 **
- 内存预警:wx.onMemoryWarning
- 回收后台页面计时器:在页面 onHide 的时候手动把定时器清理掉,有必要时再在 onShow 阶段恢复定时器
- 避免频发事件中的重度内存操作
onPageScroll
事件回调使用节流- 避免
CPU
密集型操作,譬如复杂的计算 - 避免调用
setData
,或减小setData
的数据量 - 尽量使用 IntersectionObserver 来替代 SelectorQuery,前者对性能影响更小
- 大图,长列表优化:长列表组件
参考: