了解小程序底层架构
为了更好的制定优化方案,我们有必要先了解下小程序的底层架构、以及与普通网页开发的差异
小程序最终渲染载体与当下一些热门的技术 Flutter、React Native等不同,依然是浏览器内核,而不是原生客户端。
而对于传统的网页来说,UI 渲染和 JS 脚本是在同一个线程中执行,所以经常会出现 “阻塞” 行为。微信小程序基于性能的考虑,启用了双线程模型:
- 视图层:也就是 webview 线程,负责启用不同的 webview 来渲染不同的小程序页面;
- 逻辑层:一个单独的线程执行 JS 代码,可以控制视图层的逻辑;
一、启动性能优化
小程序在整个启动过程中,一般需要完成几项工作:
1.准备运行环境
在小程序启动前,微信会先启动双线程环境,并在线程中完成小程序基础库的初始化和预执行。
2.下载,注入并执行对应小程序代码包
在小程序初次启动时,需要下载编译后的代码包到本地。如果启动了小程序分包,则只有主包的内容会被下载。另外,代码包会保留在缓存中,后续启动会优先读取缓存。
3. 渲染小程序首页
小程序代码包下载好之后,会被加载到适当的线程中执行,基础库会完成所有页面的注册。
开发者可以在第2,3步去优化小程序的启动性能
代码包大小优化
小程序在首次打开是,回去下载并执行代码包,随着代码包大小上升,耗时也会提升。
分包加载
目前小程序分包大小有以下限制:
- 整个小程序所有分包大小不超过 16M
- 单个分包/主包大小不能超过 2M
注意:1. 自定义第三方组件,需要放在主包内;2. 小程序的tab切换页,必须放在主包里。
部分页面 h5 化
小程序提供了 web-view 组件,支持在小程序环境内访问网页。当实在无法在小程序代码包中腾出多余空间时,可以考虑降级方案 —— 把部分页面 h5 化。小程序和h5的通信可以通过JSSDK或者postMessage通道来实现。
例如:项目中数据报告模块,引入图表库,增加整体包的体积。这块我们就可以采用web-view改造
减少代码包中的静态资源文件
小程序代码包体积有限,建议开发者把图片、视频等静态资源都放在 CDN 上。去除冗余样式文件,项目中采用gulp构建,通过gulp-clean清除一些冗余的样式文件
二、渲染性能优化
小程序渲染过程
1. 准备新的 webview 线程环境,包括基础库的初始化;
2. 从逻辑层到视图层的初始数据通信;
3. 视图层根据逻辑层的数据,结合 WXML 片段构建出节点树(包括节点属性、事件绑定等信息),最终与 WXSS 结合完成页面渲染;
由于微信会提前开始准备 webview 线程环境,所以小程序的渲染损耗主要在后两者 数据通信 和 节点树创建/更新 的流程中。相对应的,比较有效的渲染性能优化方向就是:
- 降低线程间通信频次;
- 减少线程间通信的数据量;
- 减少 WXML 节点数量;
合并setData调用
尽可能的将多次setData合并成一次,降低线程间的通讯频率,
只把与界面渲染相关数据放在data中,
setData传输的数据越大,线程间通信的耗时越长,渲染速度就越慢
例如项目中,取消选中城市的id这个值与wxml渲染无关,我们就不需要放置在data中。
精简首屏数据
推荐开发者延迟请求非关键渲染数据,缩短网络请求时延,与视图层渲染无关的数据尽量不要放在 data 中,以免传输垃圾数据,加快首屏渲染完成时间。
避免阻塞渲染
在小程序启动流程中,会顺序执行app.onLaunch, app.onShow, page.onLoad, page.onShow, page.onReady,所以,尽量避免在这些生命周期中使用Sync结尾的同步API,如 wx.setStorageSync,wx.getSystemInfoSync 等。
控制WXML节点数
建议一个页面使用少于 1000 个 WXML 节点,节点树深度少于 30 层,子节点数不大于 60 个。一个太大的 WXML 节点树会增加内存的使用,样式重排时间也会更长,影响体验。
去除不必要的事件绑定
尽量减少在会被频繁触发的用户事件,例如onPageScroll, 这种会被频繁触发的用户事件,会使通信过程频繁发生。我们实在是要在项目中使用,最好在onPageScroll事件回调使用节流;
开启图片缓存
开启 HTTP 缓存控制后,下一次加载同样的图片,会直接从缓存读取,大大提升加载速度
图片懒加载、雪碧图(CSS Sprite)优化
这两者都是比较老生常谈的图片优化技术,这里就不打算细讲了。
小程序的 image 组件 自带 lazy-load 懒加载支持。
利用本地缓存
小程序提供了wx.setStorageSync等异步读写本地缓存的能力,数据存储在本地,返回的会比网络请求快。例如项目中可以将一些用户信息存到缓存中。退出登录需要记得清楚缓存
使用自定义组件
比如项目中“上传图片与视频”可以单独抽出来,做成一个组件,组件的更新并不会影响页面上其他元素的更新;各个组件也将具有各自独立的逻辑空间。每个组件都分别拥有自己的独立的数据、setData调用。
回收页面计时器
在页面 onHide 的时候手动把定时器清理掉,有必要时再在onShow阶段恢复定时器。虽然说区区一个定时器回调函数的执行,对于系统的影响应该是微不足道的,但不容忽视的是回调函数里的代码逻辑,譬如在定时器回调里持续setData大量数据,就会严重影响js线程。
例如项目中创意保存草稿功能,需要利用定时器轮询保存。
总结