在过去的几天里,我一直在与我创建的应用程序中的FlatList的性能问题作斗争。
FlatList由静态标头和x行组成。在测量的情况下,有62行,每行包含4个组件 - 其中一个使用4次,总计最多7个行单元。这个列表的每一个细胞或者是TouchableNativeFeedback或TouchableOpacity(用于测试目的,这些都高度() => null
到onPress。有shouldComponentUpdate上几级(列表容器,行,单细胞)中使用,我想渲染性能够用了那种清单。
为了进行一致的测量,我使用了initialNumToRender={data.length}
,因此整个列表一次呈现。列表使用按钮呈现,数据加载不是我测量的一部分 - 它被预加载到组件本地状态。
根据附带的Chrome Performance Profile JS线程需要1.33秒来渲染组件。我已经使用CPU减速x6来更准确地模拟Android设备。
但是,列表显示在设备上大约15秒标记,因此从按钮按下到列表显示的实际渲染需要超过14秒!
我想弄清楚的是JS渲染组件和组件实际出现在屏幕上之间会发生什么,因为设备是 整个那个时候反应迟钝。每个触摸事件都已注册,但只有当列表最终显示在屏幕上时才会播放。
我已经附加了chrome dev工具的跟踪,使用android systrace工具进行的systrace和来自android profiler的屏幕(遗憾的是我无法找到导出后者的选项)。
跟踪几乎同时运行 - 订单是systrace,android profiler,chrome dev工具。
我应该采取哪些措施来帮助我了解应用冻结时发生了什么?
Simple reproduction application (code in src.js, first commit)
答案 0 :(得分:3)
查看source code you posted,我不认为这是React渲染问题。
问题在于,您在var comment = $$('#comment').getValue() + post_id.toString();
方法和渲染过程中调用的辅助方法中做了太多工作。
每次在数组上调用render
,.filter
或.forEach
时,整个列表都会重复.map
次。对n
组件执行此操作时,计算复杂度为m
。
例如,这是O(n * m)
渲染方法:
TransportPaymentsListItem
在示例代码中有11个这样的迭代器调用。对于您使用过的62行数据集,数组迭代器的总计为 4216 次!即使每个迭代器都是非常简单的比较,只需遍历所有这些列表太慢并阻塞主JS线程。
要解决此问题,您应该在组件链中将状态转换提升到更高的位置,这样您只能进行一次迭代并构建一个视图模型,您可以向下传递组件树并以声明方式呈现而无需额外的工作。
答案 1 :(得分:1)
我现在尝试了不同的东西,我甚至考虑包装原生的Android RecyclerView,但公平地说,这似乎是一个挑战,因为我之前没有使用原生Android代码的经验。
过去几天我尝试过的其中一件事就是使用react-native-largelist,但它未能实现承诺的性能提升。公平地说,它可能比FlatList
更慢,但我没有采取精确的测量。
经过几天的谷歌搜索,编码和分析我终于得到了this Medium post,它引用了recyclerlistview包,它似乎提供了比FlatList更好的体验。对于已分析的案例,渲染时间降至约2秒,包括300毫秒的JS线程工作。
必须注意到初始渲染的改进来自减少的渲染项目数量(在我的情况下为11)。 FlatList
initialNumToRender={11}
FlatList
设置最初会在同一时间内呈现。
在我的情况下,初始渲染虽然仍然很重要,但并不是唯一重要的事情。较大列表的recyclerlistview
性能下降主要是因为滚动时它会将所有渲染的行保留在内存中,而console.log
会回收渲染的行以放入新数据。
重新渲染的观察者性能改进的原因实际上很容易测试。我在行组件中添加了shouldComponentUpdate
到recyclerlistview
,并计算了实际重新呈现的行数。对于我的行高和测试设备分辨率FlatList
,只重新渲染17行,而shouldComponentUpdate
触发数据集中每个项目的recyclerlistview
。值得注意的是,FlatList
重新呈现的行数不依赖于数据集大小。
我的结论是,recyclerlistview
性能可能会因较大的数据集而降低,而TouchableNativeFeedback
速度应该保持在相似的水平。
recyclerlistview
里面的swal({
title: 'Are you sure?',
text: "You won't be able to revert this!",
type: 'warning',
showCancelButton: true,
cancelButtonColor: '#d33',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Yes, delete it!'
}).then(function(result){
if (result.value) {
// perform the AJAX request
}
});
似乎也更敏感,因为动画会毫不拖延地启动,但我无法解释这种看待分析器的行为。
我的行组件肯定还有改进的余地,但是现在我对整体列表渲染性能感到满意。
Simple reproduction application with recyclerlistview (code in src.js, second commit)
答案 2 :(得分:0)
只需再提供一个源即可尝试。
您可以像这样在android / app / build.gradle中启用Hermes。
project.ext.react = [
entryFile : "index.js",
enableHermes: true, // clean and rebuild if changing
]
我的应用程序没有崩溃,但经过一些繁琐的操作后,可触摸设备将停止工作。发生这种情况时,进程仍可以正常运行,应用程序的抽屉仍在工作。但是我的应用程序中所有可触摸的对象都停止工作,没有任何错误消息。我花了三天时间找到解决方案,最后尝试了爱马仕。在react native docs上显示
Hermes is an open-source JavaScript engine optimized for running React Native apps on Android.