GitChat 作者:Li Luo
原文:征服React Native—列表组件
关注公众号:GitChat 技术杂谈,一本正经的讲技术

前言

移动应用往往受限于屏幕大小,而数据内容长度的不确定性,在很多地方都需要用列表组件来作为数据展示的容器。对应于原生应用组件,它可能是iOS的TableView,也可能是Android的ListView,RecycleView。这些组件都有一些共同的特点:

视图可滚动。

可复用视图模板。

视图高度随着数据内容长度的变化而弹性变化。

自带性能优化。

当你在纠结要不要用列表组件的时候,可以考虑一下,你使用的场景是否需要具备以上的属性,尤其是性能优化。

在每一列视图的高度都较低的情况下,在手机上一屏的显示内容一般不过7列左右,且不说大部分时候我们的UI不会出这种让人心累的设计,而是我们不能预测出API到底会返回多少组数据回来。所以,掌握列表组件的使用,是作为移动开发必须掌握的一项基本技能。

下面,就让我们来看看React Native的框架之下,又有哪些列表组件可供使用呢。

认识列表组件

ListView

是React Native最早诞生的列表组件,可以方便的用来显示具有纵向滚动属性的数据,实现最基本的两个属性 dataSource 和 renderRow就能让它工作起来。它也支持更多高级的属性,如section和sticky section headers, header,footer,onEndReached等,以及一定的性能优化。为了使ListView滚动更加平滑,在动态的加载一个大的数据(无尽列表)时,可以这样一些优化:

  • 只优化发生变化的列:rowHasChanged就是通过比较数据是否发生变化,来判断ListView的row是否需要重绘。

  • 限定行渲染的速度:默认每次只渲染一行(可以由pageSize属性来控制),把工作分解为较小的块,以减少渲染时丢帧的几率。

基本用法:

<ListViewdataSource={this.state.dataSource}renderRow={(rowData, sectionID, rowID) => this.cell(rowData, rowID)}
/>

但是,ListView在处理无尽列表时,表现却不尽人意,它并不会把视图以外的元素从VirtualDom上面移除,在列表长度长度较大时,滚动时往往出现掉帧情况,内存也占用随着列表的滚动,消耗急剧增加。如上图中所示。

NOTE 从使用者的角度来看,FlatList和SectionList是ListView的一次裂变。不过它们并不是ListView所派生出来,而是同属于VirtualizedList的具体实现,比起ListView,它们在性能上做了极大的改进,最早出现在0.43版本,但在该版本中的bug较多,如果想要使用建议升级RN到0.44以上。

FlatList

顾名思义,它是一个扁平化的列表,砍掉了section的支持,同时,增加了很多移动端常用的玩法:支持横向滑动,下拉刷新,separator,ScrollToIndex等。相比ListView,性能上也得到了巨大的提升,一般情况下,推荐使用FlatList。基本用法:

<FlatList
  data={[{key: 'a'}, {key: 'b'}]}renderItem={({item}) => <Text>{item.key}</Text>}
/>

SectionList

如果需要把列表进行分类展示,同时给每个分类设置头部,比如像地址,分类的产品,分类的相册等,SectionList就是最好的选择。

基本用法:

<SectionListrenderItem={({item}) => <ListItem title={item.title} />}renderSectionHeader={({section}) => <H1 title={section.key} />}sections={[ // homogenous rendering between sections
​    {data: [...], key: ...},
​    {data: [...], key: ...}]}
/>

VirtualizedList

如果你需要更强的定制化的列表,RN的FlatList和SectionList已经不能满足你要的效果,可以在VirtualizedList上增加Wrapper来实现你的定制化。

虚拟化通过维护有限宽度的渲染窗口,并把渲染窗口之外的所有item替换为空,这样大大提高了大型列表的内存消耗和性能,滚动起来也更加的流畅。

常用属性

data:源数据,默认为Array<{key: string}>类型。

renderItem:对应一个数据Item的显示。

ListHeaderComponent:列表头部。

ListFooterComponent:列表尾部。

ListEmptyComponent:当列表为空的时候,显示的component.

horizontal:是否水平方向展示。

onEndReached:已经滚动到底部的callback.

onRefresh:下拉刷新的callback.

refreshing:标记是否正在刷新。

initialNumToRender:如果有“回到顶部”的需求,建议设置该属性,建议为刚好满屏时候的列数。

Multi-column

如果要实现GridView的效果,你可以FlatList自带的属性numColumns和columnWrapperStyle配合使用,实现多列:

  <FlatListhorizontal={this.state.horizontal}data={this.props.data}numColumns={2}columnWrapperStyle={styles.multiColumns}renderItem={({ item, index }) => this.props.renderRow(item, index)}/>

下拉刷新

ReactNative在新的列表组件当中,已经提供了下拉刷新的功能,可以通过快速的设置refreshing和onRefresh方法来实现:

  <FlatListdata={this.props.data}renderItem={({ item, index }) => this.props.renderRow(item, index)}refreshing={this.state.refreshing}onRefresh={() => {this.setState({refreshing: true})this.props.getProducts(this.state.pageIndex).then((items) => {this.setState({refreshing: false})}).catch((error)=> Alert.alert(error.message))}}/>

超长列表的优化

对于列表的优化,主要集中在两个方面,一个是内存消耗,一个用户响应,用户响应又可以分为:滚动是否流畅,对点击等操作响应速度是否迅速。我们先来看看新的列表组件VirtualizedList都给我们带了哪些改进:

PureComponent: 减少不必要的渲染,如果props属性不变,它就不会重绘。 这里需要我们确保在更新props后不是===,否则UI可能无法更新更新。

限定渲染窗口: 通过维护有效项目的有限渲染窗口并把渲染窗口之外的所有元素替换为空(Blank),大大提高了大型列表的内存消耗和性能。

低优先级渲染窗口以外的区域:窗口适应滚动行为,如果项目远离可见区域,则项目将以低优先级(在任何运行的交互之后)逐渐呈现,否则为了最小化查看空格的可能性。。

异步渲染:内容将异步地渲染在屏幕外。 这意味着可能滚动会比填充率更快,看到空白的内容。

可以看到,新的列表组件在内存消耗上做出了改进,滚动的流畅度得到了较大的提升,但是,对于用户点击等操作的响应的速度应该算是没有带来利好,反而在滚动中会出现白屏。

从截图中可以看到,FlatList在流畅度的提升还是很明显的,不过,在急速滚动的情况下,中间会出现白屏,这对于用户体验上来说很不友好。这里,我们可以参考VirtualizedList提供的属性来做优化。

windowSize: 限定绘制的最大数目,默认为21。

maxToRenderPerBatch:一次绘制的最大数目。

updateCellsBatchingPeriod:更新绘制的间隔时间。

removeClippedSubviews:移除看不见的subview,目前还有bug,可酌情使用。

initialNumToRender:首次绘制的数目。

getItemLayout:可以用来帮助我们跳过高度和位置的重新运算,当我们的每一个Item高度一致时,设置这个属性可以极大的提高渲染效率。

getItemLayout={(data, index) => ({length: ITEM_HEIGHT, offset: (ITEM_HEIGHT+ SEPARATOR_HEIGHT) * index, index}
)}

不过尝试了几种以上属性的组合,感觉并不能解决很好的解决白屏问题,这个问题的修复只能期待更新的版本,大家也可以尝试主动提交PR。

以上的操作都是VirtualizedList提供的方法,那对于ListView的卡顿问题,我们也可以模仿FlatList的做法去改进:

置空非显示区域的元素:把已经移出屏幕的Item给置空。

一次加载多个元素:增加一次绘制的元素个数。

重用列表项:限定只渲染指定的N个item,从N+1之后就重用之前创建的Item。

RN列表组件特别愚蠢的一点是,对于移出界面的节点它并不会去销毁,而是依然保留在Dom结构上,这里,新鲜出炉VirtualizedList也并没有解决问题。

可展开的多级列表

如果只是为了实现类似于Android的ExpandableList的展开收拢效果,而且只有一级子目录的情况下,SectionList就已经可以满足我们的需求,只需要为SectionListHeader绑定onPress事件,在事件中修改展开收拢的状态即可。

如果是多级的列表,比如像这样的地址: 北京.朝阳区.望京街道.XX大厦C栋… 像这种层级比较深的情况下,我们可以用什么方法快速实现呢?这种情况下,建议大家采用FlatList。有兴趣一起探讨的同学,欢迎在6月5日加入我们的在线讨论。

你还需要知道的事

目前的列表组件,不管是Android还是iOS平台,都还是比较顺畅而且使用方便,如果不是对用户体验有着特殊要求,以上的内容应该已经可以满足大部分的使用情况。不过,它们也都有各自的缺陷。在处理超长列表的时候,ListView的短板在于滑动卡顿和内存消耗大,FlatList的问题就在于快速滑动时,可能会看到空白的区域,始终都不尽人意。

除了在RN的组件上做优化,我们还能为我们的列表体验优化做点什么呢?

  1. 分页展示:尽量不要一次性加载过多的数据,可以的话,给你的数据加上分页加载,比如一次10条数据,让用户主动去下拉刷新,这个用FlatList来实现还是非常简便的。

  2. 使用Native原生组件:需要注意的是,React Native为我们提供了可以跨平台使用的列表组件,它们并不是对应的原生组件,所以它们并不是直接使用到了原生组件的特性,从源码来看,FlatList只是用到原生的ScrollView及其事件,但并没有使用到原生RecycleView带来的好处。所以,如果你的项目对支持超长列表以及用户体验要求高的双重要求,你完全可以选择原生组件,这样,才能更好的利用平台特性。

参考阅读

  • 使用Enzyme测试React(Native)组件|洞见。

  • 前端组件化开发方案及其在React Native中的运用。

  • 文中代码示例。

作者简介:罗丽,ThoughtWorks高级软件工程师,高级软件工程师,移动技术开发顾问,能熟练在项目中运用TDD及Refactor等技术,拥有丰富的软件开发经验, 多年海内外项目交付的经历,擅长Android,iOS等多平台开发技术,目前任职于ThoughtWorks海外事业部,曾在多个大型移动应用架构中担任技术顾问。


实录:《罗丽:React Native列表组件实战解析》


彩蛋

重磅 Chat 分享:《一场 Chat 让你搞清 BAT 程序员的技术职级》

分享人:
胜洪宇,一线互联网公司前端技术组长,掘金签约作者,前端博客博主,所讲课程帮助超过20万前端小伙伴学习。
Chat简介:
很多程序员向往进入 BAT 这样的大型互联网公司,但是又不知道他们如何评定技术职级。
- 阿里集团薪资职级如何划分?让你快速得到马云的青睐。
- 在百度明白这些,你将快速晋升。
- 腾讯职级里的小秘密,这样工作你会更强。
一场 Chat 让你搞清 BAT 的技术评价体系,为您进入超级互联网公司指明技术方向,时刻做好准备!如果您希望您的技术团队也像这些互联网巨头一样强大,本场 Chat 我将帮您马上模仿建立有效的技术职级体系。

想要免费参与本场 Chat ?很简单,「GitChat技术杂谈」公众号后台回复「BAT」

GitChat · 移动开发 | 征服React Native—列表组件相关推荐

  1. Android之React Native 中组件的生命周期

    React Native 中组件的生命周期 概述 就像 Android 开发中的 View 一样,React Native(RN) 中的组件也有生命周期(Lifecycle).所谓生命周期,就是一个对 ...

  2. 移动跨平台框架React Native状态栏组件StatusBar【16】

    前端江太公 React Native,是一个混合移动应用开发框架,是目前流行的跨平台移动应用开发框架之一.React Native 采用不同的方法进行混合移动应用开发.它不会生成原生 UI 组件,而是 ...

  3. React Native 下载组件以及npm常用命令

    一.React Native 下载组件: RN的组件都是需要从网上下载的.正常来说,我们通过npm start打开服务器之后,直接用npm下载即可.常用的组件,例如按钮,滚动等,都是可以直接下载的.下 ...

  4. 怎么安装aptdaemon模块_自己开发一个React Native 模块

    大纲 为什么需要 React Native Module 如何创建一个 React Native的模块 编写 Android Toast 功能模块 如何调试 React Native 模块------ ...

  5. 【React Native开发】React Native控件之DrawerLayoutAndroid抽屉导航切换组件解说(13)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50599951 本文出自:[江清清的博客] (一)前言 [好消息]个人 ...

  6. 【React Native开发】React Native控件之RefreshControl组件具体解释(21)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50672747 本文出自:[江清清的博客] (一)前言 [好消息]个人 ...

  7. 【React Native开发】React Native控件之TextInput组件讲解与QQ登录界面实现(11)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50589570 本文出自:[江清清的博客] (一)前言 [好消息]个人 ...

  8. React Native列表视图FlatList使用优化实践指南

    列表视图在app中是非常常见的,目前React Native比较严重的性能问题集中在FlatList大列表等地方,以下通过js层的优化,甚至原生层的优化封装,使性能媲美原生. FlatList Rea ...

  9. React Native常用组件之ListView

    1. ListView常用属性 ScrollView 相关属性样式全部继承 dataSource ListViewDataSource 设置ListView的数据源 initialListSize n ...

最新文章

  1. IntelliJ IDEA 自动编译功能无法使用,On 'update' action:选项里面没有update classes and resources这项...
  2. kafka 批量 回写mysql_kafka往mysql写数据操作笔记
  3. 北京soul_打破虚拟迎接现实,“Soul”让网络社交楚楚不凡
  4. 阿里巴巴的五大平台野心,让“连接”论成为过去式
  5. 判断字符串是否为JSON
  6. deb下载路径、apt下载路径(deb路径、apt路径)、自定义打包deb文件、安装deb文件、解压deb文件
  7. Mybatis-Plus 使用自定义注入器后,查询条件中不再添加逻辑删除字段限定条件
  8. Python中的图书管理系统
  9. [转载]多维数组与Json格式的转化
  10. CLR中的程序集加载
  11. 前端框架MVVM是什么(整理)
  12. 使用函数判断完全平方数
  13. Android地图跑步项目,通过Weex 300行代码开发一款简易的跑步App
  14. VC6.0 中文代码显示乱码的问题
  15. adb 查看固件版本
  16. Java源码阅读--任重而道远(lang)
  17. 如何解决C盘实际空闲空间远大于可压缩卷空间
  18. 2021年全球手机市场最大赢家除了苹果,还有OPPO系
  19. 按自己的需要获取对象中的属性
  20. org.hibernate.PersistentObjectException: detached entity passed to persist: cn.edu.xupt.bean.Users

热门文章

  1. HOJ-1983-Beautiful numbers
  2. java毕业设计“小蜜蜂”校园代取快递系统(附源码、数据库)
  3. java不可以修改的修饰语,在Java程序中,用关键字_修饰的常量对象创建后就不能再修改了。...
  4. [lammps命令]thermo_style 命令详解(1)
  5. 10万行级excel批量导入
  6. 大数据分析案例-基于多元线性回归算法预测学生期末成绩
  7. 更适合用c语言编程的情况是,不大适合用汇编语言编程,更适合用C语言编程的情况是( )。...
  8. ubuntu双屏幕显示设置
  9. jpa怎么实现新增获取id_java-JPA:如何根据ID以外的字段值获取实体?
  10. cdo matlab,CDO学习1 CDO简介