大列表渲染卡顿时,React 项目里怎么优化?
满分答案#
大列表卡顿的核心原因是一次性渲染大量 DOM 节点,阻塞主线程。 优化方案按优先级:虚拟滚动(核心)、懒加载、分页、列表项 memo 优化、减少渲染节点、时间切片、避免复杂计算,其中虚拟滚动是解决超大数据量最有效的方案。
一、卡顿的根本原因#
- 一次性渲染成百上千个 DOM 节点
- 主线程被 JS 执行阻塞,页面交互、滚动、输入都会卡顿
- 重排重绘(reflow)成本极高
二、React 项目实战优化方案(从必用到进阶)#
1. 虚拟滚动(最有效、首选)#
只渲染可视区域内的节点,可视区域外的 DOM 全部不渲染,节点数从 10000 → 10~20。
成熟库(直接用)
react-window(轻量)react-virtualized(功能全)antd/antd-table自带virtual属性
import { FixedSizeList } from 'react-window';
const VirtualList = ({ data }) => {
return (
<FixedSizeList
height={500} // 可视区域高度
width="100%"
itemCount={data.length}
itemHeight={50} // 每项高度
>
{({ index, style }) => (
<div style={style}>{data[index]}</div>
)}
</FixedSizeList>
);
};jsx2. 列表项使用 React.memo 缓存#
防止父组件更新导致所有列表项重复渲染。
const Item = React.memo(({ item }) => {
return <div>{item.name}</div>;
});jsx3. 分页加载 / 滚动加载(懒加载)#
不一次渲染所有数据:
- 分页
- 滚动到底加载更多(无限滚动)
useEffect(() => {
// 监听滚动,触底加载下一页
window.addEventListener('scroll', handleScroll);
}, []);jsx4. 使用正确、稳定的 key#
千万不要用 index 做 key,会导致:
- 错误复用
- 大量不必要的 DOM 销毁重建
- 列表 Diff 极慢
// ✅ 正确
{item.id}
// ❌ 错误
{index}jsx5. 时间切片(Time Slicing)#
利用 React Fiber 把渲染任务分片执行,不阻塞主线程:
ReactDOM.createRoot(React 18 默认开启)- 配合
useDeferredValue/Suspense
import { useDeferredValue } from 'react';
const deferredList = useDeferredValue(list);jsx6. 减少列表项内部计算#
用 useMemo 缓存列表项内部的过滤、排序、格式化。
const content = useMemo(() => formatData(item), [item]);jsx7. 图片懒加载#
列表中的图片延迟加载,减少渲染压力。
三、优化优先级#
- 虚拟滚动(1000条以上数据必用)
- 分页/无限加载
- 列表项 memo 优化
- 稳定 key
- 时间切片(React 18)
- 减少内部计算 & 图片懒加载
四、一句话总结#
大列表卡顿是因为一次性渲染太多 DOM。 最优方案是虚拟滚动,只渲染可视区域; 其次是分页、懒加载、memo 缓存、稳定 key, 配合 React 18 时间切片,可彻底解决卡顿问题。
总结#
万条数据不卡顿 = 虚拟滚动 + memo + 正确key + 时间切片