背景
用过antd table的可能会发现当设置单页显示200条以上数据的时候,无论是点击分页栏切换下一页,还是进行全选或取消全选功能,都会变得明显卡顿,当然这里的明显因人而异。
用一张图来展示效果:
这是当表格单页展示了500条数据并点击复选框进行全选时的性能截图,可以看到左下方饼图里,整个全选过程花费了6684ms,接近7秒的时间。就,还挺卡的。
寻找解决方案
带着疑问打开了antd官网,找到Table组件。哎呦,不错哦~看到了“虚拟列表”四个字,似乎是官方提供的解决方案,那就来看看吧。“虚拟列表”方案的主要思想,是把Table组件的body部分用react-window的虚拟列表组件进行覆盖,以达到徒有Table其表但内里是虚拟列表的“两手都抓”的准备。Table组件理所应当的给我们准备了components属性:
既然方案都有了,那就照着官网开始改造自己的代码吧~
首先,引入相应组件:
import { VariableSizeGrid as Grid } from 'react-window';
接着,将components的body覆盖:
components={{
body: renderVirtualList
}}
//摘去了一些代码
const renderVirtualList = (rawData, {scrollbarSize,ref,onScroll})=>{
return (
<Grid
columnWidth={index=>mergedColumns[index].width} //宽度要设置或计算好
columnCount={mergedColumns.length}
rowCount={rawData.length}
rowHeight={54}
width={1000} //根据实际需要,或和官网一样监听屏幕尺寸计算
>
{({columnIndex,rowIndex,style})=>(
<div style={style}> //style必须要有,不然滚动会出现白屏
{{rawData[rowIndex][mergedColumns[columnIndex].dataIndex]}}
</div>
)}
</Grid>
)
}
官网的rawData是[1,2,3,4,5,...,99999,100000]这样一个数组,表格展示的是数组的索引,而实际开发中表格展示的可能是文字、可能是图标。这样的话该怎么写呢?可以从columns入手。在使用Table组件时,一般做法就是先定义好columns数组,columns数组里可以通过render函数定义每一个单元格的渲染方式,所以我们只需要调用一下这个方法即可:
{columns[columnIndex].render(rawData[rowIndex][columns[columnIndex].dataIndex], rawData[rowIndex])}
这样就可以看到和修改前一样的表格内容了。
处理勾选列
“等等,我的表格里第一页明明是复选框列,修改后怎么没啦?”
是的。由于表格的body项是一整个被重写了,所以原来开箱既有的rowSelection中定义的效果都失效了。这时我们只能动动小手自己往上加了。笔者的笨方法还是通过columns下手,但实际components属性还提供了其他属性(见:其他)用来修改表格的其他部位,但是笔者这时已经满足了业务需要,就没有再继续探索了...所以,嗯:将columns第一列设置成:
{
title: <Checkbox onChange={handleCheckAll} checked={xxx} indeterminate={xxx}/>
dataIndex: 'id',
render:(value,record)=><Checkbox onChange={e=>handleCheck(e,value,record)} checked={xxx}/>
}
通过将首列和首列的标题都渲染成复选框,可以进行勾选操作。 经过一步步改造,最后可以收获一个单页展示500条数据,但是也不卡顿的表格了~
其他
贴下components的其他属性:
export interface TableComponents<RecordType> {
table?: CustomizeComponent;
header?: {
wrapper?: CustomizeComponent;
row?: CustomizeComponent;
cell?: CustomizeComponent;
};
body?: CustomizeScrollBody<RecordType> ||
{
wrapper?: CustomizeComponent;
row?: CustomizeComponent;
cell?: CustomizeComponent;
};
}