更快!彻底优化 React 应用
在 Tolgee 平台,我们管理翻译。因此,我们最重要的视图是一个翻译列表,每一行都包含不同语言的关键翻译和相关翻译。由于此视图服务于多种用途,我们有很多需求,而满足所有这些需求需要进行大量的优化,今天我想与大家分享一下。
我们正在处理什么
总体来说,整个视图使用了无限滚动,当用户向下滚动时,我们会自动获取额外的数据。每一行都需要一些按钮,以便用户编辑、选择或修改翻译状态。我们有搜索和过滤器,它们应该能够快速运行。然后,我们有可调整的列,允许用户通过鼠标拖动来更改整个布局。我们还需要检测垂直方向的溢出文本,这不能简单地通过 CSS 来实现,我们需要先“测试渲染”它,然后看看它是否合适。最后但同样重要的一点(因为新功能肯定会在未来推出),所有功能都需要在焦点状态下运行,以便用户可以通过键盘控制。
如何识别一般问题
基本上,当你的 React 应用程序运行缓慢时,总是因为太多组件重新渲染或渲染频率过高。React 提供了非常实用的开发者工具,你可以用它来分析你的应用,然后找出哪些组件的渲染时间最长。
当我识别出渲染速度慢的组件时,我需要理解渲染速度如此缓慢的确切原因。
有人可能会认为,如果我们保持组件规模较小,渲染量应该是可控的,但事实证明并非如此。主要问题在于组件通常相互依赖(例如父子组件或使用相同的数据),当状态的微小变化导致应用程序的大量代码重新渲染时,你可能需要应用额外的优化,而 React 不会自动执行这些优化。
为了确保我的优化确实有效,我带着我的超级旧笔记本电脑去上班,我认为这是最可靠的改进基准
一次解决一个问题
现在,我将尝试以我们的应用为例,阐述不同的情况,并提供我们选择的解决方案。理解具体情况至关重要,因为优化通常需要权衡——你用舒适度(优雅/便捷/通用性)来换取速度。因此,如果你进行了错误的优化,可能什么也做不了,只会毁了你的代码。
优化状态管理
当我们简单地使用 Context API 作为主状态时,每次更改都会重新渲染几乎整个视图。这个问题在我们使用搜索字段时最为明显,因为输入会变得非常慢。
您可以使用一些状态管理工具(例如 Redux、MobX 等)来解决这个问题,或者您可以巧妙地使用上下文,从而最大限度地减少重新渲染(查看我之前的文章)。
大量子组件重新渲染
巧妙的状态管理仍然无法完全避免不必要的重新渲染。在某些情况下,组件更新会导致所有子组件重新渲染,代价可能过于高昂。你可以用React.memo
- 包裹每个子组件来避免这种情况,这样 React 只会在 props 发生变化时渲染子组件。但是,请务必在文档中完全理解它的工作原理,然后检查它是否真的有帮助。
在我们的例子中,我们用 包裹列表中的每一行React.memo
。由于行主要依赖于获取的数据,因此我们可以节省大量不必要的重新渲染。
无限滚动
我们现在的主要问题是我们的页面可以包含无限数量的组件。解决这个问题的一个方法是只渲染当前用户可见的元素。最好使用一些第三方解决方案,因为正确实现起来相当复杂。经过反复尝试,我们最终选择了 库react-list
。
这类库通常要求您预先指定组件的大小(因为它们需要计算可滚动区域的整个长度,而无需实际渲染内容),这可能会造成很大问题 - 在我们的例子中,行大小是可变的,因此在渲染之前我们无法真正知道它的大小。react-list
可以通过仅要求您估计行的大小来解决这个问题,然后在实际渲染元素时自动更新大小。这可能会导致滚动条跳动(因为在滚动时容器的高度会发生变化),但如果您的估计比较合理,那么这只是个小问题。
太棒了!现在我们把组件的显示数量从无限减少到了“取决于你的屏幕尺寸”。然而,我们又遇到了一个问题……
降低行复杂度
我们的无限滚动优化并非没有代价。渲染每一行都需要耗费大量时间,而且由于用户滚动速度很快,这可能会导致内容跟不上滚动速度。与此相关的问题是我们的可调整大小布局,它的速度也非常慢。
这是一个相当棘手的问题,因为唯一能做的就是降低每行的复杂度。在我们的案例中,我们巧妙地运用了 CSS 来解决了这个问题grid
,这使得我们只用很少的 HTML 元素就能创建复杂的布局。
另一件事是避免在首次渲染时显示不必要的内容。所有按钮默认都是隐藏的,只有鼠标悬停在指定行上时才会显示。我们之前只用 CSS 来实现,这对 React 没有任何帮助。所以我的解决方案是真正隐藏按钮(不渲染它们),只显示它们onmouseover
,这显著改善了情况。
这个解决方案带来了一个新问题:由于按钮实际上不存在,你无法使用焦点tab
。我解决这个问题的方法是始终渲染第一个和最后一个按钮(但用 CSS 隐藏它们),而其他按钮会在焦点位于行内或鼠标悬停时渲染——这样用户就不会注意到区别。
结论
当我意识到我们的应用程序变得非常慢时,我非常害怕,最后我花了整整一周的时间进行这些优化,但经过反复试验,我找到了薄弱环节并能够针对它们进行改进。考虑到它的复杂性,即使在我那台超级老旧的大学笔记本电脑上,它也能很好地运行,我学到了很多东西。
我现在是一名快乐的开发人员,我花了相当多的工作时间只是在我们的应用程序中滚动。
文章来源:https://dev.to/tolgee_i18n/faster-optimizing-react-app-to-the-bone-21kc附言:查看Tolgee.io并给我们github stars