一点小改动,让你的React渲染提升160倍!

147 阅读5分钟

一点小改动,让你的React渲染提升160倍!

大家好,我是梦兽。今天给大家分享一个小改动,可以让你的React渲染提升160倍。关注梦兽编程公众号了解更多WEB开发技巧。

React通常被认为是一个高性能的框架,但这里的“通常”意味着有时你会感到受限,因为React中的重新渲染问题可能导致性能下降。当你尝试创建大型或复杂的组件,其中包含大量动态元素和数据操作时,确保在这样的环境中创建高性能的用户界面可能会成为挑战。如果没有正确的工具和关于React的知识,你很容易遇到问题。然而,大多数情况下,React是相当快速和可靠的,这也是它如此流行的原因。

问题

在我之前在公司工作时,遇到的一个问题是,我需要渲染一个包含非常多列和行的表格,比如一个2000行乘以200列的表格。如果我们计算一下这个大小的表格需要多少个DOM节点,那将是200000个DOM节点。

Google 推荐使用 DOM:

  • 节点总数少于1,500个
  • 最大深度为32个节点
  • 父节点的子节点数不超过60个

Untitled.png

对于那些刚开始阅读 Chrome 开发工具内存图的人来说,让我告诉您上图显示了堆大小的内存不间断增长(蓝线)。绿线显示了 DOM 中节点数量的增长。这两点都表明该应用程序远远超过了安全线的限制,即大约1500个节点和32最大深度。

所有这一切的结果是,我们的应用程序通常在 Chrome 中使用 100MB 到 200MB RAM 运行良好,但需要完整的4.9GB -5.2 GB!只是为了渲染带有表格的一页。

为什么 DOM 节点需要这么多内存?

那么系统中的所有内容都需要一定量的内存来运行它。在这种情况下,我们正在考虑 HTML DOM 节点,节点的平均大小取决于每个节点用于保存内容的平均字节数,例如 UTF-8文本、属性名称和值或缓存信息。

想象一下,智能手机为文档对象模型 (DOM) 分配 1 GB 内存,因为它通常将4GB 内存总量中的3GB 用于标准操作。为了进行估计,可以考虑基于此分配的每个节点的平均内存使用情况。

每个节点40个字符的内部文本每个字符2字节,每个字符 2 个字节,用于 4 个属性值,每个属性值 10 个字符,每个字符 1 个字节,用于 4 个属性名称,每个属性名称 4 个字符,160 字节用于 C/C++ 节点开销。在这种情况下 N(worst_case),最坏情况最大节点:

= 1,024 X 1,024 X 1,024
  / (2 X 40  +  2 X 4 X 10  +  1 X 4 X 4  +  160)

= 3,195,660.190476.

无论如何,处理所有这些也花费了大量时间,例如,对于 2000 x 200 表的大小,它花费了大约 34.97 秒(计算和渲染所花费的总时间,其中渲染是主要瓶颈)+ 14.31 秒(我的时间花费的时间)系统(M1 Mac book air)渲染初始列表)。所以总共 49.28 秒!!!只是为了看到表加载然后崩溃:(

为了解决这个问题,我们都会使用虚拟化。

一种在DOM中部分渲染内容的技巧,只渲染用户可见的节点。也就是说,假设用户一次只能看到10行和10列,我们就只渲染这么多内容。这对系统来说既简单又快速。

Untitled 1.png

如果用户向上或向侧面移动,我们会在用户滚动时在运行时渲染更多的行和列,并不断销毁不再出现在用户屏幕上的旧节点,这样我们就能够为浏览器维护安全数量的节点以显示且不会超出内存。

对于那些好奇的人们来说,没错!虽然虚拟化相对于一次性渲染所有元素可能需要更多的计算资源,但它在性能、可扩展性、内存效率和用户体验方面的优势往往使其成为首选,尤其是在处理大规模或动态数据集的应用中。

React-window

为了在 React 中做到这一点,我们可以使用一组很好的库。其中之一是 react-window 

Untitled 2.png

在我们的例子中,我们有一个复杂的要求,我必须在滚动时冻结某些列和行,上面的示例也显示了一个列表,但是当您渲染更大的表格时,您应该使用 VariableSizeGrid 它将您的行和列视为单独的单元格就像一个矩阵,只渲染那些用户可见的单元格。

使用虚拟化后,页面加载时间从 49 秒缩短到 300 毫秒,减去网络延迟(具体取决于您的互联网)。

嗯,确实如此,做了一些小的更改,例如使用 useMemo 和 useCallback 来优化更多的东西,但大部分性能是通过使用简单的概念获得的。