在 React 生态中,有多个成熟的 虚拟列表 npm 库,无需从零实现复杂的 DOM 复用、滚动计算逻辑。以下是最常用、功能最完善的 3 个库,附带安装、基础用法和核心特性对比,方便你直接选型使用:
一、推荐库排行(按易用性 + 功能完整性)
| 库名 | 特点(核心优势) | 适用场景 | 周下载量(npm) |
|---|---|---|---|
react-window |
轻量(仅 3KB)、API 简洁、性能优异,支持固定/可变高度/宽度 | 大部分场景(列表/表格/下拉框) | 约 300 万+ |
react-virtualized |
功能全面(支持表头、排序、筛选、网格)、生态成熟,react-window 的"增强版" |
复杂表格、带交互的大型列表 | 约 200 万+ |
react-big-list |
专为超大数据量(10万+)优化,支持惰性加载、无限滚动,内存占用低 | 海量数据列表(如日志、报表) | 约 5 万+ |
二、具体使用教程(最常用:react-window)
1. 安装
bash
npm install react-window # 核心库
# 可选:如需滚动条美化、触摸支持,安装辅助库
npm install react-window-infinite-loader # 无限滚动
npm install react-virtualized-auto-sizer # 自适应容器尺寸
2. 基础用法:固定高度列表(最常用场景)
jsx
import React from 'react';
import { FixedSizeList } from 'react-window';
// 模拟 10 万条数据
const mockData = Array.from({ length: 100000 }, (_, index) => `列表项 ${index + 1}`);
// 渲染单个列表项
const ListItem = ({ index, style }) => {
return (
<div style={style} className="list-item">
{mockData[index]}
</div>
);
};
// 虚拟列表组件
const VirtualList = () => {
return (
<div style={{ height: '500px', width: '300px', border: '1px solid #eee' }}>
<FixedSizeList
height={500} // 列表容器高度(必须)
width="100%" // 列表容器宽度(必须)
itemCount={mockData.length} // 总数据量
itemSize={50} // 单个列表项高度(固定)
>
{ListItem}
</FixedSizeList>
</div>
);
};
export default VirtualList;
3. 进阶用法:可变高度列表
如果列表项高度不固定(如富文本、动态内容),使用 VariableSizeList:
jsx
import { VariableSizeList } from 'react-window';
// 模拟不同高度的列表项(随机 30-80px)
const getRandomHeight = () => Math.floor(Math.random() * 50) + 30;
const itemHeights = Array.from({ length: 100000 }, getRandomHeight);
const VariableHeightListItem = ({ index, style }) => {
return (
<div style={style} className="list-item">
{mockData[index]}(高度:{itemHeights[index]}px)
</div>
);
};
const VariableHeightVirtualList = () => {
return (
<div style={{ height: '500px', width: '300px', border: '1px solid #eee' }}>
<VariableSizeList
height={500}
width="100%"
itemCount={mockData.length}
itemSize={index => itemHeights[index]} // 动态返回当前项高度
>
{VariableHeightListItem}
</VariableSizeList>
</div>
);
};
4. 无限滚动(结合 react-window-infinite-loader)
适合数据量极大、需要分页加载的场景:
bash
npm install react-window-infinite-loader
jsx
import React, { useState, useCallback } from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
// 模拟异步加载数据(如接口请求)
const fetchData = async (page) => {
// 实际项目中替换为真实接口
return new Promise(resolve => {
setTimeout(() => {
const newData = Array.from({ length: 20 }, (_, index) => `第 ${page} 页 - 列表项 ${index + 1}`);
resolve(newData);
}, 500);
});
};
const InfiniteVirtualList = () => {
const [data, setData] = useState([]);
const [page, setPage] = useState(1);
const [hasNextPage, setHasNextPage] = useState(true); // 是否还有下一页
const [isNextPageLoading, setIsNextPageLoading] = useState(false); // 是否正在加载下一页
// 加载下一页数据
const loadMore = useCallback(async () => {
if (isNextPageLoading || !hasNextPage) return;
setIsNextPageLoading(true);
try {
const newData = await fetchData(page);
setData(prev => [...prev, ...newData]);
setPage(prev => prev + 1);
// 模拟只有 5 页数据
if (page >= 5) setHasNextPage(false);
} catch (err) {
console.error('加载失败:', err);
} finally {
setIsNextPageLoading(false);
}
}, [page, isNextPageLoading, hasNextPage]);
// 标记某项是否已加载(无限滚动核心)
const isItemLoaded = index => !hasNextPage || index < data.length;
// 强制加载下一页(滚动到底部触发)
const loadMoreItems = useCallback(() => {
loadMore();
}, [loadMore]);
return (
<div style={{ height: '500px', width: '300px', border: '1px solid #eee' }}>
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={hasNextPage ? data.length + 1 : data.length} // 预留一个"加载中"项
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
ref={ref}
onItemsRendered={onItemsRendered} // 监听可视区域项变化,触发加载
height={500}
width="100%"
itemCount={hasNextPage ? data.length + 1 : data.length}
itemSize={50}
>
{({ index, style }) => {
// 加载中状态
if (!isItemLoaded(index)) {
return <div style={style} className="loading">加载中...</div>;
}
// 正常数据项
return <div style={style} className="list-item">{data[index]}</div>;
}}
</FixedSizeList>
)}
</InfiniteLoader>
</div>
);
};
export default InfiniteVirtualList;
三、其他库快速入门
1. react-virtualized(功能更全)
适合复杂场景(如带表头的表格、网格布局):
bash
npm install react-virtualized
基础表格示例:
jsx
import React from 'react';
import { VirtualizedTable } from 'react-virtualized';
const columns = [
{ key: 'id', label: 'ID', width: 80 },
{ key: 'name', label: '名称', width: 150 },
{ key: 'desc', label: '描述', width: 200 },
];
const data = Array.from({ length: 10000 }, (_, index) => ({
id: index + 1,
name: `项目 ${index + 1}`,
desc: `这是第 ${index + 1} 个项目的描述信息`,
}));
const VirtualizedTableComponent = () => {
return (
<div style={{ height: '600px', width: '100%' }}>
<VirtualizedTable
width={800}
height={600}
headerHeight={40}
rowHeight={50}
rowCount={data.length}
rowGetter={({ index }) => data[index]}
columns={columns}
/>
</div>
);
};
2. react-big-list(超大数据量优化)
bash
npm install react-big-list
基础用法:
jsx
import React from 'react';
import { BigList } from 'react-big-list';
const data = Array.from({ length: 500000 }, (_, index) => `超大数据项 ${index + 1}`);
const BigVirtualList = () => {
return (
<div style={{ height: '500px', width: '300px' }}>
<BigList
data={data}
height={500}
width="100%"
itemHeight={40}
renderItem={({ item }) => <div style={{ padding: '10px' }}>{item}</div>}
/>
</div>
);
};
四、选型建议
- 优先选
react-window:轻量、易用、性能足够,覆盖 90% 场景(普通列表、下拉选择器、简单表格); - 复杂表格/网格选
react-virtualized:支持表头固定、列排序、筛选等,适合后台管理系统; - 超大数据量(10万+)选
react-big-list:内存占用更低,渲染更流畅。
所有库均支持 React 16+ 版本,且有完善的 TypeScript 类型定义,可放心在生产环境使用~