|
|
|
---
|
|
|
|
order: 30
|
|
|
|
title:
|
|
|
|
en-US: Virtual list
|
|
|
|
zh-CN: 虚拟列表
|
|
|
|
---
|
|
|
|
|
|
|
|
## zh-CN
|
|
|
|
|
|
|
|
通过 `react-window` 引入虚拟滚动方案,实现 100000 条数据的高性能表格。
|
|
|
|
|
|
|
|
## en-US
|
|
|
|
|
|
|
|
Integrate virtual scroll with `react-window` to achieve a high performance table of 100,000 data.
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
import { VariableSizeGrid as Grid } from 'react-window';
|
|
|
|
import ResizeObserver from 'rc-resize-observer';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { Table } from 'antd';
|
|
|
|
|
|
|
|
function VirtualTable(props) {
|
|
|
|
const { columns, scroll, className } = props;
|
|
|
|
const [tableWidth, setTableWidth] = React.useState(0);
|
|
|
|
|
|
|
|
const widthColumnCount = columns.filter(({ width }) => !width).length;
|
|
|
|
const mergedColumns = columns.map(column => {
|
|
|
|
if (column.width) {
|
|
|
|
return column;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...column,
|
|
|
|
width: Math.floor(tableWidth / widthColumnCount),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const gridRef = React.useRef<any>();
|
|
|
|
const [connectObject] = React.useState<any>(() => {
|
|
|
|
const obj = {};
|
|
|
|
Object.defineProperty(obj, 'scrollLeft', {
|
|
|
|
get: () => null,
|
|
|
|
set: (scrollLeft: number) => {
|
|
|
|
if (gridRef.current) {
|
|
|
|
gridRef.current.scrollTo({ scrollLeft });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
});
|
|
|
|
|
|
|
|
const resetVirtualGrid = () => {
|
|
|
|
gridRef.current.resetAfterIndices({
|
|
|
|
columnIndex: 0,
|
|
|
|
shouldForceUpdate: false,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
React.useEffect(() => resetVirtualGrid, []);
|
|
|
|
React.useEffect(() => resetVirtualGrid, [tableWidth]);
|
|
|
|
|
|
|
|
const renderVirtualList = (rawData: object[], { scrollbarSize, ref, onScroll }: any) => {
|
|
|
|
ref.current = connectObject;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Grid
|
|
|
|
ref={gridRef}
|
|
|
|
className="virtual-grid"
|
|
|
|
columnCount={mergedColumns.length}
|
|
|
|
columnWidth={index => {
|
|
|
|
const { width } = mergedColumns[index];
|
|
|
|
return index === mergedColumns.length - 1 ? width - scrollbarSize - 1 : width;
|
|
|
|
}}
|
|
|
|
height={scroll.y}
|
|
|
|
rowCount={rawData.length}
|
|
|
|
rowHeight={() => 54}
|
|
|
|
width={tableWidth}
|
|
|
|
onScroll={({ scrollLeft }) => {
|
|
|
|
onScroll({ scrollLeft });
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{({ columnIndex, rowIndex, style }) => (
|
|
|
|
<div
|
|
|
|
className={classNames('virtual-table-cell', {
|
|
|
|
'virtual-table-cell-last': columnIndex === mergedColumns.length - 1,
|
|
|
|
})}
|
|
|
|
style={style}
|
|
|
|
>
|
|
|
|
{rawData[rowIndex][mergedColumns[columnIndex].dataIndex]}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Grid>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ResizeObserver
|
|
|
|
onResize={({ width }) => {
|
|
|
|
setTableWidth(width);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Table
|
|
|
|
{...props}
|
|
|
|
className={classNames(className, 'virtual-table')}
|
|
|
|
columns={mergedColumns}
|
|
|
|
pagination={false}
|
|
|
|
components={{
|
|
|
|
body: renderVirtualList,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</ResizeObserver>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Usage
|
|
|
|
const columns = [
|
|
|
|
{ title: 'A', dataIndex: 'key', width: 150 },
|
|
|
|
{ title: 'B', dataIndex: 'key' },
|
|
|
|
{ title: 'C', dataIndex: 'key' },
|
|
|
|
{ title: 'D', dataIndex: 'key' },
|
|
|
|
{ title: 'E', dataIndex: 'key', width: 200 },
|
|
|
|
{ title: 'F', dataIndex: 'key', width: 100 },
|
|
|
|
];
|
|
|
|
|
|
|
|
const data = [];
|
|
|
|
|
|
|
|
for (let i = 0; i < 100000; i += 1) {
|
|
|
|
data.push({
|
|
|
|
key: i,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
ReactDOM.render(
|
|
|
|
<VirtualTable columns={columns} dataSource={data} scroll={{ y: 300, x: '100vw' }} />,
|
|
|
|
mountNode,
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
|
|
|
<style>
|
|
|
|
.virtual-table .ant-table-container:before,
|
|
|
|
.virtual-table .ant-table-container:after {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
.virtual-table-cell {
|
|
|
|
box-sizing: border-box;
|
|
|
|
padding: 16px;
|
|
|
|
border-bottom: 1px solid #e8e8e8;
|
|
|
|
background: #FFF;
|
|
|
|
}
|
|
|
|
[data-theme="dark"] .virtual-table-cell {
|
|
|
|
box-sizing: border-box;
|
|
|
|
padding: 16px;
|
|
|
|
border-bottom: 1px solid #303030;
|
|
|
|
background: #141414;
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|