列表是大多数Web应用程序不可或缺的一部分,因为它们有助于以更合理的格式显示数据。但是,当应用程序尝试处理列表中的过多数据时,通常会导致性能问题。
在本指南中,我们将概述与巨大的列表相关的一些问题,并逐步解决在React应用程序中克服这些性能挑战的步骤。
先决条件
要继续学习本教程,您需要具备以下条件。
- 对JavaScript和React的一般理解
- npm大于v5.2或yarn
- node版本12或更高
问题
让我们创建一个示例应用程序,以演示当尝试呈现10,000条记录的大型列表时,应用程序的性能和DOM树会发生什么。
启动您的终端并粘贴下面的代码以创建一个React应用程序。
npx create-react-app render-list
运行以下代码以安装Faker库,我们将使用该库来生成要在我们的应用程序中使用的随机数据。
npm i faker
接下来,转到目录中的App组件,src然后输入以下代码。
import React from 'react';
import faker from 'faker'
import './App.css';
const data = new Array(10000).fill().map((value, index) => ({ id: index, title: faker.lorem.words(5), body: faker.lorem.sentences(4) }))
function App() {
return (
<div>
{data.map(((item) => (
<div key={item.id} className="post">
<h3>{item.title} - {item.id}</h3>
<p>{item.body}</p>
</div>
)))}
</div>
);
}
export default App;
转到文件并在下面添加代码行,以向列表中添加一些样式。App.css
.post{
background-color: #eee;
margin: 2rem;
padding: 1rem;
}
.pagination{
margin: 1rem auto;
list-style: none;
display: flex;
justify-content: space-evenly;
width: 50%;
}
.active{
border: 1px solid black;
border-radius: 100%;
padding: 0 3px;
outline: none;
}
上面的代码呈现了10,000条记录的列表。
在浏览器中启动React应用程序并打开控制台。

渲染列表示例
页面加载后,您会注意到呈现列表需要花费时间。滚动时还有明显的滞后。
以下是解决React应用中与列表相关的性能问题的四种方法。
1.分页
分页使您可以在页面中呈现数据,而不是一次呈现所有信息。这样,您基本上可以控制页面上显示的数据量,因此您不必对DOM树施加过多的压力。
React中的大多数UI库都带有分页组件,但是如果您想快速实现分页而不需要安装UI库,则可能需要检出react-paginate。该库呈现了一个分页组件,该组件接受一些props,可帮助您浏览数据。
要安装该库,请在终端中运行以下代码。
npm i react-paginate
安装后,您可以修改App组件以对数据进行分页而不是立即渲染。将下面的代码粘贴到您的App组件中。
import React, { useState, useEffect } from 'react';
import ReactPaginate from 'react-paginate';
import faker from 'faker'
import './App.css';
function App() {
const [pagination, setPagination] = useState({
data: new Array(1000).fill().map((value, index) => (({
id: index,
title: faker.lorem.words(5),
body: faker.lorem.sentences(8)
}))),
offset: 0,
numberPerPage: 10,
pageCount: 0,
currentData: []
});
useEffect(() => {
setPagination((prevState) => ({
...prevState,
pageCount: prevState.data.length / prevState.numberPerPage,
currentData: prevState.data.slice(pagination.offset, pagination.offset + pagination.numberPerPage)
}))
}, [pagination.numberPerPage, pagination.offset])
const handlePageClick = event => {
const selected = event.selected;
const offset = selected * pagination.numberPerPage
setPagination({ ...pagination, offset })
}
return (
<div>
{pagination.currentData && pagination.currentData.map(((item, index) => (
<div key={item.id} className="post">
<h3>{`${item.title} - ${item.id}`}</h3>
<p>{item.body}</p>
</div>
)))
}
<ReactPaginate
previousLabel={'previous'}
nextLabel={'next'}
breakLabel={'...'}
pageCount={pagination.pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={5}
onPageChange={handlePageClick}
containerClassName={'pagination'}
activeClassName={'active'}
/>
</div>
);
}
export default App;
在此示例中,我们以分页状态存储负责分页的详细信息。我们只渲染当前数据,而不是一次渲染数据,而是通过基于当前偏移量和要在页面上显示的记录数对主数据进行切片来获得当前数据。
该ReactPaginate组件接受事件处理程序作为props,每当页面更改时都会调用该事件处理程序。事件处理程序计算当前偏移量,然后将其用于计算页面加载时要显示的当前数据。
以下是添加了分页功能后该应用程序外观的屏幕截图。

列表以react-paginate分页
2.无限滚动
渲染大量数据的另一种方法是无限滚动。无限滚动涉及在向下滚动列表时将数据追加到页面的末尾。最初加载页面时,仅加载一部分数据。向下滚动页面时,将附加更多数据。
有几种方法可以在React中实现无限滚动。就个人而言,我更喜欢使用react-infinite-scroll-component。要安装它,请在终端中运行以下代码。
npm i react-infinite-scroll-component
打开您的App组件并粘贴以下内容。
import React, { useState } from 'react';
import faker from 'faker'
import InfiniteScroll from 'react-infinite-scroll-component';
import './App.css';
function App() {
const data = new Array(1000).fill().map((value, id) => (({
id: id,
title: faker.lorem.words(5),
body: faker.lorem.sentences(8)
})))
const [count, setCount] = useState({
prev: 0,
next: 10
})
const [hasMore, setHasMore] = useState(true);
const [current, setCurrent] = useState(data.slice(count.prev, count.next))
const getMoreData = () => {
if (current.length === data.length) {
setHasMore(false);
return;
}
setTimeout(() => {
setCurrent(current.concat(data.slice(count.prev + 10, count.next + 10)))
}, 2000)
setCount((prevState) => ({ prev: prevState.prev + 10, next: prevState.next + 10 }))
}
return (
<InfiniteScroll
dataLength={current.length}
next={getMoreData}
hasMore={hasMore}
loader={<h4>Loading...</h4>}
>
<div>
{current && current.map(((item, index) => (
<div key={index} className="post">
<h3>{`${item.title}-${item.id}`}</h3>
<p>{item.body}</p>
</div>
)))
}
</div>
</InfiniteScroll>
);
}
export default App;
基本上,这里发生的是每当用户滚动到页面末尾时,它都会检查该hasMore属性是否为false。如果不是,它将附加更多数据到页面。它会继续将数据追加到页面的末尾,直到该hasMore属性变为false为止。
3。 react-virtualized
react-virtualized专为呈现大型列表和表格数据而设计。它使用类似于无限滚动的技术,称为窗口化。使用窗口时,只有列表的可见部分才会呈现到屏幕上。
与上述解决方案相比的一个优势是其丰富的有用组件,其中包括:react-virtualized
CollectionGridListMasonryfTable
要安装 ,请启动您的终端并运行以下命令。react-virtualized
npm i react-virtualized
转到您的App组件,然后粘贴下面的代码。
import React from 'react';
import faker from 'faker'
import { List } from "react-virtualized";
import './App.css';
function App() {
const data = new Array(1000).fill().map((value, id) => (({
id: id,
title: faker.lorem.words(5),
body: faker.lorem.sentences(8)
})))
const renderRow = ({ index, key, style }) => (
<div>
<div key={key} style={style} className="post">
<h3>{`${data[index].title}-${data[index].id}`}</h3>
<p>{data[index].body}</p>
</div>
</div>
)
return (
<List
width={1200}
height={700}
rowRenderer={renderRow}
rowCount={data.length}
rowHeight={120}
/>
);
}
export default App;
该List组件采用width和heightprops来设置窗口的尺寸。它还采用rowHeightprop,它代表列表中每个项目的高度rowCount,而代表数组的长度。rowRenderer采用负责呈现每一行的功能。
react-virtualized带有许多其他处理大列表的选项。
4, react-window
react-window是用于在React中有效呈现大型列表的一组组件。该库的完整重写react-virtualized旨在解决与大小和速度有关的缺点。react-window还涵盖了比react-virtualized更多的案例。
通过在终端中运行以下代码进行安装react-window。
npm i react-window
转到您的App组件,然后将代码替换为下面的代码。
import React from 'react';
import faker from 'faker'
import { FixedSizeList as List } from "react-window";
import './App.css';
function App() {
const data = new Array(1000).fill().map((value, id) => (({
id: id,
title: faker.lorem.words(5),
body: faker.lorem.sentences(8)
})))
const Row = ({ index, key, style }) => (
<div>
<div key={key} style={style} className="post">
<h3>{`${data[index].title}-${data[index].id}`}</h3>
<p>{data[index].body}</p>
</div>
</div>
)
return (
<List
width={1400}
height={700}
itemCount={data.length}
itemSize={120}
>
{Row}
</List>
);
}
export default App;
该代码与react-virtualized的代码非常相似。我们使用了一个List组件,该组件接受一组定义列表的props,并传入一个Row组件函数,该函数负责呈现列表中的每一行。
结论
处理大型列表时,重要的是不要一次渲染所有数据,以避免DOM树超载。改善性能的最佳方法取决于您的用例。如果您希望将所有数据都放在一个位置,则无限滚动或开窗技术将是您的最佳选择。否则,您可能希望使用分页解决方案,该解决方案可将数据分为不同的页面。