一、现象
小程序大列表在windows上可以使用,在mac上列表渲染白屏。
二、分析
数据量小环境可以正常使用,可以排除业务逻辑的问题。与其他环境差相比,排查出无法渲染的环境与其他环境数据量不在同一个数量级,可以确定是代码的某个地方处理存在性能瓶颈。
环境 | 数据量 |
---|---|
开发(无法渲染) | 34 * 401 |
测试(可渲染) | 4 * 5 |
仿真(可渲染) | 3 * 10 |
生产(可渲染) | 5 * 5 |
经过一系列的测试,可以定位到出问题的代码是如下红框中圈出的部分
三、方案探索
- 这部分代码中比较明显的是扩展运算符(...)的性能问题,扩展运算符(...)与传统的对象方法Object.assign和数组的合并方法concat相比,存在非常明显的性能差异,从下图可以看出,在处理大数据方面,传统方法的性能远远好于扩展运算符(...),以下测试数据是10w左右的一维数组,具体测试细节详见:juejin.cn/post/701744…
顺着这个思路做的第一个改动抛弃扩展运算符,采用传统的方式进行对这部分代码进行改写
- 改完测试发现并不生效,且进一步测试发现,只要存在如下代码,后续的状态更新都不生效(在页面上展示currentIndex和afterCode都不是最新值),从而定位到数据量大、数据结构较为复杂的情况下,setData的性能瓶颈在MAC上被触发。
this.setData({
todoData: _load.todoData
});
// 以下代码不生效
this.setData({
currentIndex,
afterCode,
});
进一步分析发现,除了在事务中心page页存在多次更新todoData,在其子组件List中,还存在对其进行二次加工生生成viewList和dropdownData,分别用来渲染列表和筛选器,这就导致了更加严重的性能问题。
- 尝试优化todoData数据结构,并试图减少数据量进行测试,结果发现即使删掉了很多不用的属性,且最大数据量只有34 * 50的时候依然没有办法正常渲染列表。
- 采用微信小程序提供的recycle-view(长列表)组件重构这部分代码,recycle-view主要用来解决如下问题:
- 列表数据很大,首次 setData 的时候耗时高
- 渲染出来的列表 DOM 结构多,每次 setData 都需要创建新的虚拟树、和旧树 diff 操作耗时都比较高
- 渲染出来的列表 DOM 结构多,占用的内存高,造成页面被系统回收的概率变大。
其核心思路是通过createRecycleContext
方法创建RecycleContext
对象来替代 setData 管理数据,并且只渲染显示在屏幕的数据,不需要渲染的数据留一个空的 div 占位元素。采用recyle-view重构的效果是在mac上可以正常打开,滚动加载更多也是没有问题的。
四、结论
- recycle-view可以解决mac无法打开复杂数据大列表的问题(原因:setData管理数据存在性能瓶颈)
- recycle-view相较setData,在性能方便有很大的提升,如果采用此方案可以从一定程度上提列表的渲染速度,提升用户体验
- recycle-view只能在page中使用,不能再component中使用
注: 模拟器性能相较真机较差,在较新的手机上现有方案并没有模拟器上如此明显的延迟效果。