将 React 表格​​渲染优化 160 倍!!!

2025-06-10

将 React 表格​​渲染优化 160 倍!!!

React“总体上”是一个高性能框架……注意“总体上”这个词。是的,这是因为在 React 中,你有时会感到非常受限,因为所有的重新渲染都会导致性能问题。当你尝试创建一个大型/复杂的组件时,你会发现其中有很多移动部件和数据需要左右移动,而要创建一个高性能的 UI 有时会成为一个挑战,如果你没有合适的工具和相关知识,你很快就会在 React 中自食其果。但在大多数情况下,它非常快速可靠,因此很受欢迎。

问题

之前我在公司工作时遇到的一个问题是,我需要渲染一个包含大量行和列的表格。例如,假设我们要处理一个2000 x 200(行 x 列)大小的表格。

现在,如果我们计算该大小的表需要多少个 DOM 节点,那么大约需要 200000 个 DOM 节点。

什么是 DOM 节点?
您的 Web 浏览器会解析 HTML 文档并构建一个易于理解和管理的文档对象模型 (DOM)。这种格式的 DOM 节点易于 JavaScript 等脚本语言理解和修改。

Google 推荐 DOM:

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

现在,由于我们正在处理一个200000 个节点大小的 DOM 树,您可以想象会发生什么。

惊喜惊喜!!

该图显示了 Chrome 开发工具

对于那些刚开始阅读 Chrome 开发工具内存图表的人来说,我来告诉你,上图显示了堆大小的内存持续增长(蓝线)。绿线显示了 DOM 中节点数量的增长。这两条线都表明该应用已经远远超过了安全线的极限,安全线大约是 1500 个节点,最大深度为 32。

所有这些的结果是,我们的应用程序通常在 Chrome 中使用 100MB 到 200MB 的 RAM 就能正常运行,但现在却需要整整4.9 GB - 5.2 GB !!! 才能渲染带有表格的那个页面。

我几乎无法记录内存使用情况,因为大多数时候页面都会崩溃并且不允许我对其进行任何操作。

为什么 DOM 节点占用这么多内存?

好吧,系统中的任何东西都需要一定的内存来运行。这里我们讨论的是 HTML DOM 节点,节点的平均大小取决于每个节点用于保存内容(例如 UTF-8 文本、属性名称和值或缓存信息)的平均字节数。

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

  • 每个节点 40 个内部文本字符,每个字符 2 个字节
  • 每个字符 2 个字节,共 4 个属性值,每个属性值 10 个字符
  • 4 个属性名称,每个字符占 1 个字节,每个属性名称有 4 个字符
  • C/C++ 节点开销为 160 字节

在这种情况下,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.
Enter fullscreen mode Exit fullscreen mode

在 stack overflow 上发现了一个很棒的帖子,它详细解释了这一点,上面的估计也是从那里得到的https://stackoverflow.com/questions/42590269/safe-maximum-amount-of-nodes-in-the-dom

无论如何,处理所有这些数据也花费了大量时间,例如,处理一个 2000 x 200 的表格,大约需要34.97 秒(计算和渲染的总时间,其中渲染是主要瓶颈)+ 14.31 秒(我的系统 (M1 Mac Book Air) 渲染初始列表所花费的时间)。所以总共花了49.28 秒!!!只是为了看看表格加载完成,然后就崩溃了 :(

还有一些统计数据支持我的观点,供您查看。

显示渲染时间统计数据的图像

欢迎在评论中进一步讨论这些问题:)

解决方案——虚拟化。

这不是您在 Windows 系统上运行的那种虚拟化,安装 Ubuntu 并感受实际操作系统的感觉。

但是有一种技术可以部分渲染 DOM 中的内容,只渲染最终用户可见的节点。所以我们尝试做的是,假设用户一次只能看到 10 行和 10 列,那么我们就只渲染这么多。这对于系统来说非常容易且快速。

图片展示了 React 虚拟化滚动

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

对于那些想知道答案的人来说,答案是肯定的!虽然与一次性渲染所有项目相比,虚拟化可能需要额外的计算资源,但它在性能、可扩展性、内存效率和用户体验方面的优势通常使其成为首选,尤其是对于处理大型或动态数据集的应用程序而言。

为了实现这一点,React 中有很多库可供使用。其中之一就是react-window

因此示例代码将是这样的:-

React 窗口包图像

该软件包将为我们提供一组非常易于使用的 API 和组件。

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

由于显而易见的原因,无法发布完整代码。因为它正在生产环境中使用。

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

因此性能和加载时间大幅提升了160 倍!!!

嗯,其实就是这样,虽然做了一些小改动,比如使用 useMemo 和 useCallback 来优化其他一些功能,但大部分性能提升都是通过这个简单的概念实现的。
顺便问一下,你知道吗,移动开发也使用同样的概念在社交媒体应用中显示大型动态信息。这是一个非常酷的技术,我很享受将它集成到我们的项目中。希望其他遇到同样问题的人会觉得这篇文章有用,或者即使你没有,在你的武器库中添置一件新武器也是不错的,你永远不知道什么时候会需要它 :)

参考

PS-我不会对上述任何信息承担任何责任,大部分信息都是我从互联网上学习和实施的。

鏂囩珷鏉ユ簮锛�https://dev.to/navneet7716/optimizing-react-table-rendering-by-160x--5g3c
PREV
您需要了解的有关 JavaScript 中的执行上下文的所有信息什么是执行上下文?
NEXT
设计模式:Vue 感觉像 React - TypeScript 🔥