关注“之家技术”,获取更多技术干货
总篇177篇 2022年第52篇
1. 项目背景
汽车之家主app前端开发广泛使用React Native技术,因为其具有跨平台可以动态更新功能。目前有类似自驾游业务线利用React Native技术在汽车之家app上实现无限瀑布流列表的功能。优化前自驾游瀑布流列表在安卓低端机上滑动多页后会出现明显卡顿现象,快速滑动屏幕整体会出现白页的情况。
优化前列表滑动效果如下所示:
正因为有上述性能问题,目前很多有瀑布流列表需求的团队使用其他技术实现。为了解决这个技术问题,我们借鉴原生可重用列表原理,实现可重用的无限瀑布流列表。
2. React Native列表渲染原理
2.1
系统列表控件加载原理
React Native提供ScrollView,ListView和FlatList控件来实现列表功能。
ScrollView可以支持横向和竖向的滚动。一次性渲染ScrollView内部所有子视图,没有做任何的懒加载。
ListView是第一代列表控件,目前已经被废弃。
FlatList是2017年官方推出的第二代列表控件。FlatList是按需加载内部控件,每次滚动过程中监听滚动位置,然后内部通过onLayout计算出展示位置的startIndex和endIndex。对于不在展示区的内容进行销毁,用空白占位。对于新出现的内容进行创建渲染。
ScrollView和FlatList滚动过程中列表加载示意图如下所示:
上面示意图3到6行表示使用scrollView控件展示40条数据,👁 表示用户正在看到的屏幕上数据的位置。第3行表示用户正在看到0到9条数据,{}表示数据已经做了加载。可以看到第3行用户看到0到9条数据,剩余的3屏数据都已经加载出来了。
上面示意图9到12行表示使用FlatList控件展示40条数据。👁 表示用户正在看到的屏幕上数据的位置。第9行表示用户正在看到0到9条数据,{}表示一屏幕的数据已经加载。可以看到第9行除了用户看到数据外后面还有一屏幕数据已经加载出来了。
第10行用户正在看第2屏数据,第1屏和第3屏数据被加载了。第11行用户正在看第3屏数据,第一屏已经释放用空白empty代替了,第2屏幕和4屏已经加载数据了。第12行用户正在看到第4屏数据,第3屏数据还在前2屏数据已经被释放用空白empty代替。
2.2
FlatList和RecyclerListView
加载原理对比
虽然FlatList是按需加载并且是异步加载的,但是也不能保证所有的JavaScript执行的渲染任务都实时地交给UI线程,立刻展示出来。如果渲染内容时间变慢就会出现白屏和卡顿现象。
从上面分析可以看出,RN自带的FlatList和原生的可重用列表控件加载原理还是有所不同的。原生的复用机制相当于把消失的列表项拿来复用,展示新的列表项。目前在github上有类似RecyclerListView控件来实现重用列表。但是目前RecyclerListView不支持瀑布流。
下图是RecyclerListView和FlatList加载的对比:
区别在于RecyclerListView在滚动过程中离开屏幕的部分没有被销毁而是拿来复用新的样式。内存回收这方面RecyclerListView做的比FlatList要好。所以我们选择RecyclerListView实现原理来实现瀑布流列表。
3. React Native瀑布流
可重用列表的实现
3.1
瀑布流展示规则
假定瀑布流为竖向两列展示。首先我们需要在列表加载前计算出所有item的宽度和高度。这里也可以给出一个模糊高度,如果高度不一致后面可以轻微的校对。瀑布流的展示规则是判断如果宽度满足一行就同行展示,如果宽度不够就换行展示。我们需要计算左右两个卡片组总的高度,新添加的卡片需要加入到总高度低的卡片组中去。
具体示意图如下所示:
3.2
瀑布流展示范围
瀑布流展示范围为屏幕可视范围加开发人员预设的距离内的内容。其余内容用空白代替。所以我们需要计算出这个范围内开始位置startIndex和结束为止endIndex。可以通过RecyclerListView里面onScroll方法回调拿到用户滚动列表的位置,通过计算列表项高度确定位置。可以参考下面的示意图:
用户每次滚动列表都在确定列表位置的变化,处理startIndex和endIdex之间瀑布流的变化。因为有缓冲区的存在所以复用区内部样式及数据调整一般对用户是不容易察觉到的。这部分和RecyclerListView内部处理是一样的。
3.3
部分核心代码如下图所示
上述函数就是完成瀑布流layout的更新。
下面我们看下如何使用瀑布流列表。首先我们需要设置瀑布流数据,需要通过dataProvider进行包装,具体代码如下所示:
我们需要把数据分成不同type,根据不同type我们需要提前告诉瀑布流列表每个卡片的宽高。具体代码如下所示:
下来是渲染部分,具体代码如下所示:
在getRow函数中渲染我们的瀑布流卡片。这里要注意这里渲染的卡片尽量要和提供给瀑布流预设高度一致,否则会计算不准确。其他的用法和FlatList大体是一致的。
3.4
瀑布流行高计算
大部分瀑布流行高不确定是文字换行导致。这里计算出文本高度就可以知道每种卡片的高度。目前有两种方法实现计算文本行高。第一种是在展示页面背后开辟一段空间去展示文本真是的宽度,用onLayout计算文本高度。等到高度都拿到后再去展示瀑布流。这种方案展示效果很好不影响滚动性能,但是前期比较耗时。
第二种方法是进行估算文本高度,这种方案实测准确率在90%以上。如果不准确瀑布流列表会进行二次修正。具体估算代码如下所示:
4. 总结和展望
通过上述优化,自驾游tab瀑布流列表在目前已有的低端机器上滑动30页数据,平均帧率在55以上,并且没有出现整块白屏页现象。具体效果参考下面的视频:
优化后性能要比之前同等数据下性能好很多。后续我们将进一步优化React Native列表性能,做到体验近似原生效果。
作者简介
盛鑫
■ C端及中台产研中心,客户端研发部。
■ 2017年加入汽车之家,目前在C端及中台产研中心的客户端研发部门,负责用车业务的开发。主要使用iOS、React Native、Flutter等大前端技术开发app。
阅读更多:
▼ 关注「之家技术」,获取更多技术干货 ▼