渲染大量数据的几种优化方案
1. 虚拟列表(推荐)
使用 react-virtualized
或 react-window
实现虚拟滚动:
import { FixedSizeList } from 'react-window';
import React from 'react';
interface ItemProps {
index: number;
style: React.CSSProperties;
}
const VirtualList: React.FC = () => {
// 列表项渲染函数
const Row = ({ index, style }: ItemProps) => (
<div style={style}>
Row {index}
</div>
);
return (
<FixedSizeList
height={400}
width={300}
itemCount={100000}
itemSize={35}
>
{Row}
</FixedSizeList>
);
};
export default VirtualList;
2. 分页加载
import { List, Button } from 'antd';
import React, { useState } from 'react';
const PagedList: React.FC = () => {
const [page, setPage] = useState(1);
const pageSize = 20;
const loadMore = () => {
setPage(prev => prev + 1);
};
return (
<List
dataSource={data.slice(0, page * pageSize)}
renderItem={item => (
<List.Item>{item}</List.Item>
)}
loadMore={
<Button onClick={loadMore}>加载更多</Button>
}
/>
);
};
3. 时间分片
使用 requestAnimationFrame
进行时间分片渲染:
import React, { useEffect, useState } from 'react';
const TimeSlicing: React.FC = () => {
const [list, setList] = useState<number[]>([]);
useEffect(() => {
// 总数据
const total = 100000;
// 每次渲染数量
const once = 200;
const render = (curTotal: number) => {
if (curTotal >= total) return;
// 计算当前需要渲染的数据
const curList = new Array(once).fill(1).map((_, index) => curTotal + index);
requestAnimationFrame(() => {
setList(prev => [...prev, ...curList]);
render(curTotal + once);
});
};
render(0);
}, []);
return (
<div>
{list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
};
4. Web Worker
将数据处理放在 Worker 线程中:
self.onmessage = (e) => {
const { data } = e;
// 处理数据
const result = processLargeData(data);
self.postMessage(result);
};
import React, { useEffect, useState } from 'react';
const WorkerList: React.FC = () => {
const [list, setList] = useState([]);
useEffect(() => {
const worker = new Worker('dataWorker.ts');
worker.onmessage = (e) => {
setList(e.data);
};
worker.postMessage(rawData);
return () => worker.terminate();
}, []);
return (
<div>
{list.map(item => (
<div key={item.id}>{item.content}</div>
))}
</div>
);
};
建议
- 优先使用虚拟列表,这是最有效的方案
- 根据业务场景选择合适的方案:
- 列表项高度固定:使用 react-window
- 高度不固定:使用 react-virtualized
- 无限加载:结合虚拟列表和分页
- 注意性能优化:
- 使用 React.memo 优化列表项渲染
- 避免在列表项中使用复杂的状态管理
- 合理使用 key 属性