在 React 中渲染大型列表对于开发人员来说可能是一项艰巨的任务。随着列表大小的增加,DOM(文档对象模型)树也会增长,从而导致渲染速度慢、滚动不流畅和内存使用率高等性能问题。在本文中,我们将讨论开发人员在渲染大型列表时面临的一些常见问题,以及可用于克服这些问题的各种解决方案。
问题陈述为什么在 Web 浏览器中渲染大量列表是一项艰巨的任务?嗯,在渲染大量项目列表时需要考虑几个因素。首先是性能,随着屏幕上要渲染的元素数量增加,浏览器的渲染引擎开始遇到性能问题。这会导致渲染速度变慢,从而导致用户界面迟缓和用户体验不佳。
处理大量元素列表需要耗费大量计算资源。滚动浏览大型列表可能会让最终用户感到不爽,最糟糕的情况是,他们可能会看到一个完全无响应的页面。由于处理能力和内存有限,对手机等低端设备的负面影响会更大。
考虑到所有这些问题,作为软件开发人员,我们需要采用优化技术并选择适当的策略。
概括本文主要讨论如何在 React 中渲染大量列表。首先,我们将定义大量列表的含义。然后,我们将研究一些可以采用的解决方案,以解决问题陈述中指出的问题。最后,我们将讨论在浏览器中渲染大量列表时需要牢记的几个关键事项。
庞大的清单是什么意思呢?
图 1:渲染大量列表的代码片段
庞大列表的定义是动态的。它完全取决于最终用户的设备。如果他们都在高端计算机或移动设备上,那么在看到任何性能限制之前,您可以在页面上安全呈现的元素数量将非常高。
另一方面,如果您的最终用户的设备比较普通,内存和处理能力有限,则此阈值将小于上述阈值。随着现代浏览器的进步,您可以轻松加载大量 DOM 元素而不必担心太多,但即便如此,如果您开始渲染包含 1,000 行或更多行的订单列表,您可以认为它是巨大的。
如何解决 React 中渲染大型列表的问题?如果您最终需要渲染数以万计的复杂列表项,则无论最终用户的硬件性能如何,您都不可避免地必须使用以下解决方案之一。首先,我们将从每个人都可以合并到其代码中的通用解决方案开始:
保持渲染树较小图 2:渲染树图解
总体而言,保持精简的渲染树可以提高整体性能,因为 DOM 元素越多,浏览器需要分配的空间就越多。此外,浏览器在布局阶段将花费更多时间。这在渲染大量列表时变得极为关键。即使减少一个 div 也能带来明显的性能差异。
最佳做法是使用最少数量的 DOM 元素来创建列表项,而不影响设计。
让我们来实验一下:
首先,我们将创建一个包含约 10,000 个元素的简单列表。以下是该列表的代码沙箱。让我们来看看该页面的 Lighthouse 性能:
图 3:海量列表的 Lighthouse 评分
首次内容绘制
0.9 秒
最大的内容丰富的油漆
3.0 秒
总阻塞时间
2.1 秒
总绩效得分
43
现在,让我们向列表项中添加一个 div 并获取所有性能数字。
以下是灯塔的表演:
图 4:带有一个额外 div 的 Lighthouse 分数
首次内容绘制
1 秒
最大的内容丰富的油漆
3.3 秒
总阻塞时间
3.3 秒
总绩效得分
40
无限滚动现在让我们进入实际的解决方案,其中第一个是使用无限滚动技术。用更简单的话来说,这种技术意味着只渲染填满整个页面长度所需的列表项,然后在用户向下滚动时添加更多项目。
我们可以使用react-infinite-scroller库来实现这一点。
以下是使用 react-infinite-scroller 实现的相同 10,000 个项目列表。
让我们看看它是如何影响我们的表现的:
图 5:无限滚动的 Lighthouse 得分
首次内容绘制
1.0 秒
最大的内容丰富的油漆
2.5 秒
总阻塞时间
160毫秒
总绩效得分
78
我们的整体绩效滚动从基数43跃升至78,这是一个巨大的进步。
这是因为我们实际上将列表分成了很小的部分。我们不是一次性渲染所有 10,000 个项目,而是只渲染在用户视图中渲染页面部分所需的前几个项目。当用户向下滚动时,react-infinite-scroller 库会将更多项目添加到 DOM。
窗口化解决该问题的另一个方法是使用窗口技术。
窗口化(或虚拟化)是一种技术,您可随时仅渲染列表中用户可见的部分。与无限滚动不同,在窗口化中,DOM 始终具有恒定数量的元素。换句话说,我们仅渲染填充用户视野所需的元素,并从列表顶部和底部移除尚未出现在视图中的元素。
图 6:窗口技术图示
当列表中的每个项目都有固定高度时,窗口化会大放异彩。这样,就可以根据滚动情况轻松计算出需要从 DOM 中添加或删除哪些项目。此外,无论用户滚动到哪里,页面的内存使用量都会保持不变,因为 DOM 元素的数量保持不变。
以下是使用 react-window 实现的相同 10,000 个项目列表。
让我们看看它是如何影响我们的表现的:
图 7:带窗口的海量列表的 Lighthouse 得分
首次内容绘制
1.0 秒
最大的内容丰富的油漆
2.5 秒
总阻塞时间
190毫秒
总绩效得分
76
要记住的关键事项使用这两种技术,您都无法执行浏览器搜索。换句话说,如果用户使用cmd + f搜索尚未呈现的文本,他们将看到 0 个匹配项(共 0 个)。为了防止这种情况,您应该在列表中添加自定义搜索或过滤选项。
与本机滚动相比,在 DOM 中添加/删除元素总是会出现轻微的延迟,但这仍然比您的应用程序最终因大量 DOM 元素而挂起要好。
创建您自己的解决方案如果您希望保持解决方案轻量并且不想使用任何库,您还可以使用Intersection Observer API实现自己的 DOM 元素延迟加载。
首先,添加足够的组件来填充您的视口。使用 getBoundingClientRect 获取视口高度,并将其除以近似的最小项目高度。为此计数加 1 以获得更好的结果。现在在其下方添加一个虚拟组件(此组件不需要显示任何内容;它只需要存在于列表的底部)。将交集可观察对象附加到此组件,并且每当此组件在视图中时,将更多项目添加到列表中以在 DOM 中呈现。这样,您最终将开发一个简单的无限滚动解决方案。
结论现在,您应该能够处理 React 应用程序中的大型列表。简而言之,您应该一次只渲染列表的一小部分,并确保视图中有足够的过滤器供用户对列表进行筛选。
作者:
Srijan Gulati
Srijan Gulati is a Senior Software Developer and the Frontend lead for Uber’s Crash analytics (Apps, services, and in-app bug reporter). He is passionate about responsive and accessible UX, problem-solving, and VIM.
Karan Verma
Karan Verma is a Senior Software Engineer and Frontend Developer specializing in data visualization at scale.
出处:https://www.uber.com/en-JP/blog/supercharge-the-way-you-render-large-lists-in-react/