在React项目中,如何优雅的优化长列表
一、虚拟列表优化长列表的原理
优化长列表的原理很简单,基本原理可以一句话概括
用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,根据滚动的 offset 大小以及所有列表元素的位置,计算在可视区应该渲染哪些元素。
具体实现步骤如下所示
- 首先确定长列表所在父元素的大小,父元素的大小决定了可视区的宽和高
- 确定长列表每一个列表元素的宽和高,同时初始的条件下计算好长列表每一个元素相对于父元素的位置,并用一个数组来保存所有列表元素的位置信息
- 首次渲染时,只展示相对于父元素可视区内的子列表元素,在滚动时,根据父元素的滚动的 offset 重新计算应该在可视区内的子列表元素。这样保证了无论如何滚动,真实渲染出的 dom 节点只有可视区内的列表元素。
- 假设可视区内能展示 5 个子列表元素,及时长列表总共有 1000 个元素,但是每时每刻,真实渲染出来的 dom 节点只有 5 个。
- 补充说明,这种情况下,父元素一般使用 position:relative,子元素的定位一般使用:position:absolute 或 sticky
二、通过 react-virtualized 来优化长列表
react-virtualized 的基础组件包含:
- Grid:用于优化构建任意网状的结构,传入一个二维的数组,渲染出类似棋盘的结构。
- List:List 是基于 Grid 来实现的,但是是一个维的列表,而不是网状。
- Table:Table 也是基于 Grid 来实现,表格具有固定的头部,并且可以在垂直方向上滚动
- Masonry:同样可以在水平方向,也可以在垂直方向滚动,不同于 Grid 的是可以自定义每个元素的大小,或者子元素的大小也可以是动态变化的
- Collection:类似于瀑布流的形式,同样可以水平和垂直方向滚动。
值得注意的是这些基础组件都是继承于 React 中的 PureComponent,因此当 state 变化的时候,只会做一个浅比较来确定重新渲染与否 。
除了这几个基础组件外,react-virtualized 还提供了几个高阶组件,比如 ArrowKeyStepper 、AutoSizer、CellMeasurer、InfiniteLoader 等,本文具体介绍常用的 AutoSizer、CellMeasurer 和 InfiniteLoader。
- AutoSizer:使用于一个子元素的情况,通过 AutoSizer 包含的子元素会根据父元素 Resize 的变化,自动调节该子元素的可视区的宽度和高度,同时调节的还有该子元素可视区真实渲染的 dom 元素的数目。(子元素会随父元素的长、宽自适应)
- CellMeasurer:这个高阶组件可以动态的改变子元素的高度,适用于提前不知道长列表中每一个子元素高度的情况。(会损耗性能,不建议使用)
- InfiniteLoader:这个高阶组件用于 Table 或者 List 的无限滚动,适用于滚动时异步请求等情况。(滚动加载)
作者:yuxiaoliang
链接:https://juejin.cn/post/6844903729460674567
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三、Vlist 的实际使用
<List loading={loading} bordered>
{" "}
// antd的List组件
<div style={{ height: 420 }}>
{" "}
// vlist父元素的高度
<AutoSizer>
{" "}
// 可根据父元素的高度动态计算出列表项的高度
{({ width, height }) => (
<VList
width={width} // 列表的宽度
height={height} // 列表的高度
rowCount={pocList.length} // 列表项数
overscanRowCount={7} // 不在视窗范围上下现实的列表项数
rowHeight={60} // 列表项的高
rowRenderer={this.renderItem} // 列表项渲染方式
noRowsRenderer={() => (
// 空数据的渲染方式
<Empty
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-60%,-50%)",
}}
/>
)}
/>
)}
</AutoSizer>
</div>
</List>
// key:数组行中的唯一值
// style: vlist的列表项的定位样式和默认样式
// parent: 参考父级Grid元素(使用CellMeasurer组件要注意:该组件会不断估算每个
// 列表项的最佳高度,所以会带来一定的性能损耗,个人建议不要使用该组件,可以像我一样将
// 列表项的高度rowHeight写死)
// index: 被渲染数据的index索引
renderItem = ({ index, key, style: styles }) => {
const { pocList } = this.state
const name = pocList[index].name || ""
return (
<List.Item style={styles} key={key}>
<Tooltip title={name}>
<Checkbox value={pocList[index].poc_id}>
{React.createElement(
"span",
null,
...this.highLightValue(ellipsis(pocList[index].name, 46))
)}
</Checkbox>
</Tooltip>
</List.Item>
)
}
其他优秀的社区库: