React长列表优化

React虚拟滚动组件

react-virtualized


官网:http://bvaughn.github.io/react-virtualized/

github: https://github.com/bvaughn/react-virtualized

列表 List

Demo: http://bvaughn.github.io/react-virtualized/#/components/List

Doc: https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md

动态高度

使用CellMeasurer组件计算列表子元素的高度。

1
import {AutoSizer, List, CellMeasurerCache, CellMeasurer} from 'react-virtualized'
1
2
3
4
5
// 宽度固定
const measureCache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 60,
})
1
2
3
4
5
6
7
8
9
10
11
12
// 每一行内容
const rowRenderer = ({ index, key, parent, style }) => {
const item = records[index]

return (
<CellMeasurer cache={measureCache} key={key} parent={parent} rowIndex={index}>
<div style={style}>
content
</div>
</CellMeasurer>
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<AutoSizer>
{({width, height}) => (
<List
width={width}
height={height}
deferredMeasurementCache={measureCache}
rowCount={records.length}
rowHeight={measureCache.rowHeight}
rowRenderer={rowRenderer}
className={styles.list}
/>
)}
</AutoSizer>

图片动态高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<CellMeasurer
cache={measureCache}
columnIndex={0}
key={key}
rowIndex={index}
parent={parent}>
{({measure}) => (
<!--若外层className带padding会导致高度计算不准确,需在内层多包裹一层z再设置padding-->
<div style={style} className={classNames}>
<img
onLoad={measure}
src={src}
/>
</div>
)}
</CellMeasurer>

更新List

参考issue:List doesn’t update display or row heights when data updated

1
2
measureCache.clearAll()
listRef.current.forceUpdateGrid()

表格 Table

Demo: http://bvaughn.github.io/react-virtualized/#/components/Table

Doc: https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md

无限滚动 + 可编辑表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import {Table, Column} from 'react-virtualized'
import {Input, Empty} from 'antd'

// 防止Input组件不必要的渲染
const MemoInput = React.memo(function (props) {
const {rowIndex, field, handleFieldChange, ...restProps} = props
return <Input {... restProps} onChange={(e) => handleFieldChange(field, e, rowIndex)} />
})

function VirtualTable(props) {
// ...

// 列, 使用useCallback优化
const nameColumn = ({cellData, rowIndex, dataKey }) => {
// ...
return (
<div>
<MemoInput
placeholder="请输入姓名"
value={cellData}
rowIndex={rowIndex}
field="姓名"
handleFieldChange={handleFieldChange}
/>
</div>
)
}

// 表头
const columnHeaderRenderer = useCallback(({dataKey}) => dataKey, [])

const rowGetter = useCallback(({index}) => dataSource[index], [dataSource])

const noRowsRenderer = useCallback(() => <Empty className={styles.empty} />, [])

return <Table
ref={tableRef}
className={styles.virtualTable}
headerClassName={styles.header}
rowClassName={styles.row}
headerHeight={TableHeaderHeight}
width={TableWidth}
height={TableHeight}
noRowsRenderer={noRowsRenderer}
rowHeight={TableRowHeight}
rowGetter={rowGetter}
rowCount={dataSource.length}
overscanRowCount={OverscanRowCount}
>
<Column
width={120}
dataKey="姓名"
headerRenderer={columnHeaderRenderer}
cellRenderer={nameColumn}
/>
</Table>
}

瀑布流 Masonry

Demo: http://bvaughn.github.io/react-virtualized/#/components/Masonry

Doc: https://github.com/bvaughn/react-virtualized/blob/master/docs/Masonry.md

react-window

react-virtualized的轻量级替代插件。

官网:https://react-window.now.sh

github: https://github.com/bvaughn/react-window