独家|React Native 无限列表的优化与实践
导语
本文介绍了在使用 React Native 开发过程中,如何对无限列表组件进行技术选型,如何使用RecyclerListView组件对无限列表进行性能优化,如何解决无限列表与标签页搭配使用时的内存优化与手势重叠的问题,希望对大家有所启发。
背景
第一,RN 官方提供的无限列表(ListView/FlatList)性能太差,一直被业内吐槽。通过实践对比,我们选择了内存管理效率更优的第三方组件——RecyclerListView。
第二,RecyclerListView 需要知道每个列表项的高度,才能正确渲染。如果,列表项高度不确定,怎么处理?
第三,标签页和无限列表组合使用时,会遇到一些问题。首先,标签页中有多个无限列表,怎样有效管理内存?其次,标签页可以左右滑动,无限列表中也有左右滚动的内容组件,二者手势区域重叠时,如何指定组件优先处理?
列表的技术选型
1. ListView
流畅度对比
衡量流畅度的关键指标是帧率,帧率越高越流畅,越低越卡顿。我们用 RecyclerListView 和 FlatList 分别实现了相同功能的无限列表,在Android 手机中进行了测试,滚动帧率如下。
实现原理对比
ListView 、FlatList、RecyclerListView 都是 RN 的列表组件,为什么它们之间性能差距这么大?我们对其实现原理进行了一些研究。
const types = {
ONE_IMAGE: 'ONE_IMAGE', // 一张图片的图文布局
TWO_IMAGE: 'TWO_IMAGE' // 两张图片的图文布局
}
{/* 把用户看不见的列表项 */}
<View style={{position: 'absolute', top: disappeared}}>
<Text>一行文本</Text>
<Image source={{uri: '1.png'}}/>
<View>
{/* 修改成用户即将看见的列表项 */}
<View style={{position: 'absolute', top: visible}}>
<Text>一行文本~~</Text>
<Image source={{uri: '2.png'}}/>
<View></View>
手势上滑,页面向上滚动,加载更多列表项(深绿色)
RecyclerListView的实践
RecyclerListView 复用列表项的位置是需要经常变化的,因此用的是绝对定位 position: absolute 布局,而不是从上往下的flex 布局。使用了绝对定位,就需要知道列表项的位置(top)。为了使用者的方便,RecyclerListView 让开发者传入所有列表项的高度(height),内部自动推断出其位置(top)。
const types = {
ONE_IMAGE: 'ONE_IMAGE', // 一张图片的图文布局
TWO_IMAGE: 'TWO_IMAGE' // 两张图片的图文布局
}
// server data
const serverData = [
{ img: ['xx'], text: '' },
{ img: ['xx', 'xx'], text: '' },
{ img: ['xx', 'xx'], text: '' },
{ img: ['xx'], text: '' },
]
// RecyclerListView state
const list = serverData.map(item => {
switch (item.img.length) {
case 1:
// 高度确定,为 100px
return { ...item, height: 100, type: types.ONE_IMAGE, }
case 2:
return { ...item, height: 100, type: types.TWO_IMAGE, }
default:
return null
}
})
const list = serverData.map(async item => {
switch (item.img.length) {
case 1:
return { ...item, height: await fontMetrics(item.text), type: types.ONE_IMAGE, }
case 2:
return { ...item, height: await fontMetrics(item.text), type: types.TWO_IMAGE, }
default:
return null
}
})
/**
* @param str 字符串文本
* @param fontSize 字号
* @returns 不折行宽度
*/
function getStrWidth(str, fontSize) {
const scale = fontSize / 17;
const capitalWidth = 11 * scale; // 大写字母
const lowerWidth = 8.6 * scale; // 小写字母
const spaceWidth = 4 * scale; // 空格
const numberWidth = 9.9 * scale; // 数字
const chineseWidth = 17.3 * scale; // 中文和其他
const width = Array.from(str).reduce(
(sum, char) =>
sum +
getCharWidth(char, {
capitalWidth,
lowerWidth,
spaceWidth,
numberWidth,
chineseWidth,
}),
0,
);
return Math.floor(width / fontSize) * fontSize;
}
/**
* @param string 字符串文本
* @param fontSize 字体大小
* @param width 渲染容器宽度
* @returns 行数
*/
function getNumberOfLines(string, fontSize, width) {
return Math.ceil(getStrWidth(string, fontSize) / width);
}
位置修正 |
标签页中的无限列表
对于分类信息流形态的产品,有的会包含多样化标签,每个标签都有特定的内容,其中大部分标签页是无限列表。如果,所有标签页的内容都同时存在,内存得不释放,也会导致性能问题。
总结
本文介绍了我们在使用 RN 开发分类信息流形态的产品的无限列表中,遇到的一些常见问题,以及如何进行技术考量、优化和选择的。希望能对大家有借鉴意义。
live
沙龙活动直播
2020年58技术沙龙活动在线直播第一弹——《大数据平台建设实践与探讨》已准备就绪,欢迎你强势围观!
详情🔎请戳👆图片查看,2月22日本周六19:00,我们不见不散。
END