内存预警
小程序提供了监听内存不足告警事件的 API:wx.onMemoryWarning[23],旨在让开发者收到告警时及时释放内存资源避免小程序 Crash。然而对于小程序开发者来说,内存资源目前是无法直接触碰的,最多就是调用 wx.reLaunch 清理所有页面栈,重载当前页面,来降低内存负荷(此方案过于粗暴,别冲动,想想就好...)。
不过内存告警的信息收集倒是有意义的,我们可以把内存告警信息(包括页面路径、客户端版本、终端手机型号等)上报到日志系统,分析出哪些页面 Crash 率比较高,从而针对性地做优化,降低页面复杂度等等。
回收后台页面计时器
根据双线程模型,小程序每一个页面都会独立一个 webview 线程,但逻辑层是单线程的,也就是所有的 webview 线程共享一个 JS 线程。以至于当页面切换到后台态时,仍然有可能抢占到逻辑层的资源,譬如没有销毁的 setInterval、setTimeout 定时器:
// Page A
Page({
onLoad() {
let i = 0
setInterval(() => { i++ }, 100)
}
})
即使如小程序的
组件,在页面进入后台态时依然是会持续轮播的。
正确的做法是,在页面 onHide 的时候手动把定时器清理掉,有必要时再在 onShow 阶段恢复定时器。坦白讲,区区一个定时器回调函数的执行,对于系统的影响应该是微不足道的,但不容忽视的是回调函数里的代码逻辑,譬如在定时器回调里持续 setData 大量数据,这就非常难受了...
避免频发事件中的重度内存操作
我们经常会遇到这样的需求:广告曝光、图片懒加载、导航栏吸顶等等,这些都需要我们在页面滚动事件触发时实时监听元素位置或更新视图。在了解小程序的双线程模型之后不难发现,页面滚动时 onPageScroll 被频发触发,会使逻辑层和视图层发生持续通信,若这时候再 “火上浇油” 调用 setData 传输大量数据,会导致内存使用率快速上升,使页面卡顿甚至 “假死”。所以,针对频发事件的监听,我们最好遵循以下原则:
onPageScroll 事件回调使用节流;
避免 CPU 密集型操作,譬如复杂的计算;
避免调用 setData,或减小 setData 的数据量;
尽量使用 IntersectionObserver[24] 来替代 SelectorQuery[25],前者对性能影响更小;
大图、长列表优化
据 小程序官方文档[26] 描述,大图片和长列表图片在 iOS 中会引起 WKWebView 的回收,导致小程序 Crash。
对于大图片资源(譬如满屏的 gif 图)来说,我们只能尽可能对图片进行降质或裁剪,当然不使用是最好的。
对于长列表,譬如瀑布流,这里提供一种思路:我们可以利用 IntersectionObserver[27] 监听长列表内组件与视窗之间的相交状态,当组件距离视窗大于某个临界点时,销毁该组件释放内存空间,并用等尺寸的骨架图占坑;当距离小于临界点时,再取缓存数据重新加载该组件。
然而无可避免地,当用户快速滚动长列表时,被销毁的组件可能来不及加载完,视觉上就会出现短暂的白屏。我们可以适当地调整销毁阈值,或者优化骨架图的样式来尽可能提升体验感。
小程序官方提供了一个 长列表组件[28],可以通过 npm 包的方式引入,有兴趣的可以尝试。
参考资料
[1]
Taro: https://taro.aotu.io/
[2]
小程序性能评分规则: https://developers.weixin.qq.com/miniprogram/dev/framework/audits/performance.html
[3]
体验评分工具(Audits 面板): https://developers.weixin.qq.com/miniprogram/dev/framework/audits/audits.html
[4]
测速系统: https://developers.weixin.qq.com/miniprogram/dev/framework/performanceReport/
[5]
JS Tree-Shaking: https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking
[6]
PurifyCSS: https://github.com/purifycss/purifycss
[7]
使用分包: https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html
[8]
独立分包: https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/independent.html
[9]
web-view: https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html
[10]
小程序开发文档: https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html
[11]
数据预拉取: https://developers.weixin.qq.com/miniprogram/dev/framework/ability/pre-fetch.html
[12]
这里: https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/operating-mechanism.html#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8
[13]
分包预下载: https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html
[14]
关键渲染路径(Critical Rendering Path): https://developers.google.com/web/fundamentals/performance/critical-rendering-path
[15]
wx.request: https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
[16]
WebP: https://developers.google.com/speed/webp
[17]
image 组件: https://developers.weixin.qq.com/miniprogram/dev/component/image.html
[18]
image 组件: https://developers.weixin.qq.com/miniprogram/dev/component/image.html
[19]
w3schools: https://www.w3schools.com/css/css_image_sprites.asp
[20]
事件循环: https://github.com/aooy/blog/issues/5
[21]
数据 diff 规则: https://nervjs.github.io/taro/docs/optimized-practice.html#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%95%B0%E6%8D%AE-diff
[22]
Web Components: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
[23]
wx.onMemoryWarning: https://developers.weixin.qq.com/miniprogram/dev/api/device/performance/wx.onMemoryWarning.html
[24]
IntersectionObserver: https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html
[25]
SelectorQuery: https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html
[26]
小程序官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html
[27]
IntersectionObserver: https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html
[28]
长列表组件: https://developers.weixin.qq.com/miniprogram/dev/extended/functional/recycle-view.html
[29]
User-centric Performance Metrics: https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics
[30]
Reduce JavaScript Payloads with Tree Shaking: https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking
[31]
小程序开发指南: https://developers.weixin.qq.com/ebook?action=get_post_info&docid=0008aeea9a8978ab0086a685851c0a
[32]
小程序官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/
[33]
Taro 官方文档: https://taro.aotu.io/home/in.html
[34]
探究WebP一些事儿: https://aotu.io/notes/2016/06/23/explore-something-of-webp/index.html