作为京东购物小程序黄金流程之一,购物车汇聚了大量的营销玩法,页面内容丰富多样。页面节点树庞大、千变万化的特点,导致性能问题比较突出且优化难度较大。在纯技术角度的性能优化达到瓶颈之后,我们开始尝试基于业务进行性能优化。
1、Performance monitor
它是小程序开发工具内置的一个可视化监控工具,能够在 OS 级别上实时记录系统资源的使用情况。
借助这个工具,可以监控 cpu 和内存占用量和波动情况,快速定位引起页面卡顿、机器发烫的模块,进而进行优化
Performances monitor
2、测试机
小程序性能分析工具较少,且开发工具的运行效果和真机差异较大,应尽量使用真机定位性能问题。
通常,我们使用 oppo r11 和 iphone 6s plus 等低档机排查性能问题,再分别挑选用户数占比较大的低、中、高档机器检验优化效果
3、监控系统
前端进行测速数据采集和上报,再通过监控系统分析页面的各项指标健康度,了解页面加载耗时情况,对性能优化有较大的参考价值。
监控系统
项目上线前,可以通过测试机检验优化效果,但测试数据毕竟不能反映用户实际情况。因此,像购物车这种,展示内容与用户强相关的页面,非常有必要使用监控系统辅助分析。
ps: 下文各项测速数据均来自监控系统。
1、购物车业务分析
2、测速数据分析
购物车数据复杂度高,因此我们重点观察了商品总数与首屏渲染耗时的关系:
结论:需进一步优化渲染策略,尽可能减少首屏渲染的商品数。
背景
早期,为缩短白屏时间,购物车使用了分屏渲染技术,把数据分为首屏和非首屏两部分,首屏渲染完成后再渲染非首屏数据。 分屏渲染最大问题在于,一旦非首屏数据量过大,渲染耗时会很长,让用户等待很长时间,最糟的情况可能引起页面假死,严重影响用户体验。 随着业务增长,这个问题带来的影响已经越来越明显,因此我们开始考虑改用分页技术
1、技术选型
难点:
技术选型
综合考虑各种业务场景和各项分页技术的特点,最终决定采用自动分页渲染技术。
2、基本思想
3、循环渲染实现方案对比
通过多次实验对比,最终我们选择时间分片模式。
4、示例
Demo: https://developers.weixin.qq.com/s/XJEDb3mP7Kex
原理: 用 raf 代替定时器,每次 setData 完成后,延迟 16.6ms 左右,再渲染下一页
实现思路:每次 setData 时触发 wxs 事件监听器,在 wxs 事件处理函数中调用 raf,raf 回调执行时调用逻辑层函数渲染下一页
流程图如下:
raf demo 流程图
5、效果对比
下面是使用分屏渲染(左图)与自动分页渲染(右图)的效果图。可操作时间缩短 50%+
分屏渲染与自动分页渲染对比
scroll-view 组件是一种滚动视图容器,它提供了一个名为 scroll-into-view 的功能,可以使视图滚动到指定元素,为方便描述,下文简称定位功能。
1、背景
购物车很多场景用到定位功能,小程序不支持 DOM 操作,使我们不得不使用 scroll-view 这个大型组件。
但是,scroll-view 组件存在较多性能问题:
节点复杂度越大,总节点数越多,scroll-view 暴露的性能问题越明显。
为了优化滚动体验,减轻 cpu 和内存压力,我们移除了这个组件的引用。
2、难点
想要移除 scroll-view,必须找到定位功能的替代方案。在微信基础库版本 2.7.3 以上版本,可使用 wx.pageScrollTo 的定位到锚点功能,对于 2.7.3 以下版本,需要自己编码实现。
示例代码:
_pageScrollTo ({ partten }) {
if (this.createSelectorQuery && wx.pageScrollTo) {
const query = this.createSelectorQuery()
query.selectViewport().scrollOffset()
query.select(partten).fields({ rect: true })
query.exec(function (res) {
if(res) {
const windowHeight = (wx.getSystemInfoSync() || {}).windowHeight || 619const offsetY = windowHeight * 0.35// 目标位置距离窗口上边界的偏移量const _scrollTop = res[0].scrollTop // 滚动条竖直滚动位置const _top = res[1].top // 目标节点上边界坐标const scrollTop = _scrollTop + _top - offsetY
wx.pageScrollTo({ scrollTop, duration: 0 })
}
})
}
}
建议关闭 wx.pageScrollTo 的滚动动画。在一些低端机器上,滚动过程中页面部分区域会白屏,且不会自动恢复。另外,基础库 2.6.4 以下版本,滚动过程中 fixed 元素会闪烁。
3、效果对比
移除 scroll-view 之后,列表滚动过程中几乎不会白屏。下面是 scoll-view 移除前后效果图。
滚动优化前后对比
指提前请求数据,打开页面后使用预请求的数据渲染
1、分类:
2、跳转时预加载
跳转时预加载
目的:缩短白屏时间。从发生跳转动作,到目标页面 onLoad 触发,中间有一段时间间隔,利用这段空闲时间提前加载数据,可缩短白屏时间
原理:触发跳转操作后,在页面发生跳转前(即调用 wx.navigateTo 之前),调用目标页面的预加载处理函数,函数触发时开始发送请求。进入目标页面后,使用提前加载好的数据渲染页面
3、预判预加载
预判预加载
目的:提前加载目标页面请求,大幅缩短目标页面白屏时间。
原理:预测用户从页面 A 进入页面 B 的可能性较大,在页面 A 内主动调用页面 B 的预加载处理函数,提前加载请求。进入页面 B 后,使用预加载数据渲染首屏
缺点:
4、效果对比
与页面 onLoad 执行才加载数据(即无预加载)相比,预判预加载首屏耗时缩短 40%,跳转时预加载首屏耗时缩短 27%。下面是无预加载(左图)与预判预加载(右图)效果图。
缓存是一种备受青睐的性能优化方法。不仅可以减少请求数,降低弱网场景空窗率,合理利用缓存甚至能使首屏耗时缩短至少 30%以上。
它适用于数据稳定性强,时效性要求低的场景,如图片、字体、配置文件等。
像购物车这种,商品列表变化比较频繁的场景能否使用缓存?
答案是肯定。
1、实现方法
实际上,购物车的数据结构比较复杂,数据量大,为避免引起性能问题,只会 setData 关键数据。但为了方便查找数据和逻辑运算,还维护了一份缓存数据,每次请求购物车接口都更新缓存。再次打开购物车页时,只要满足一定条件,就可以直接使用这份缓存数据渲染页面。
2、效果对比
缓存模式首屏时间比常规模式(页面 onLoad 时加载数据)快 44%,比预加载模式快 23%。下面是预加载模式(左图)与缓存模式(右图)效果图。
指将部分前端业务逻辑移至后台。
1、意义
终极目标:类似需求前端只开发一次,后期迭代由后端开发完成后直接上线。
2、改造过程
逻辑后移过程
参考规范:
实际上,购物车页的数据加载和渲染采用了多策略组合方式,根据场景和当前机器环境的特点动态选择数据加载和渲染策略,目的是在保障用户刚需的同时为更多用户提供更极致的体验。例如某些场景使用缓存模式+自动分页渲染,而在一些兜底场景使用的是常规模式+触底渲染等等。
本文重点在于介绍购物车页性能优化的过程和思考,没有过多深入技术实现细节,希望大家阅读后能有一些新的想法。
[1] 页面渲染机制与性能优化:https://segmentfault.com/a/1190000016458627
[2] 从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理:https://cloud.tencent.com/developer/article/1547827
[3] 高性能渲染十万条数据(时间分片):https://cloud.tencent.com/developer/article/1509390
[4] 前端性能优化之缓存利用:https://www.haorooms.com/post/cache_huancunliyong
[5] 再谈前端性能优化:https://www.cnblogs.com/cherryblossom/p/7866324.html
[6] 前端性能优化(超详细):https://www.jianshu.com/p/30c42b04623c
[7] https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
[8] https://www.w3cplus.com/javascript/requestAnimationFrame.html