具有大量数据集的 React 16.8.5 的性能问题
Performance issue with React 16.8.5 with large amount of dataset
我有一个table,它的单元格是selectable(使用react-selectable-fast)。有 15000 行,每行有 30 个 Selectable 单元格。
我在重新渲染时遇到了一个奇怪的问题。
重新渲染组件需要更长的时间。
这里是代码沙箱 link 同样的:https://codesandbox.io/s/unruffled-vaughan-vnox7
问题是,当我删除 setState
方法时,它会按预期工作,因为没有重新渲染。但是,由于需要在父组件中捕获单元格上的事件以在页面的其他形式中进行更改(在代码沙箱中不可用),所以我需要更新组件的状态,然后页面是显示所选单元格的时间比平时长。
故障可以在这里看到:https://vnox7.csb.app/
我这里有什么地方做错了吗?
React designed to be fast but can become very slow.
为什么?
问题 #1 - 在任何地方使用函数组件。
函数组件的问题是它在每次父渲染时渲染。因此,如果您在功能组件内部进行了一些繁重的计算(并且您有),您将陷入困境。即使DOM重新计算后不会更新(因为影子DOM检查),你仍然需要计算所有内容以进行比较。
看这里:
const getRows = number => {
*heavy calculation*
}
这里:
<table border="1">
<thead>
<tr>{getHead()}</tr>
</thead>
<tbody>{getRows(1500)}</tbody>
</table>
每个渲染你做同样的计算,你实际上并不需要。
如何修复: - 将功能组件更改为纯组件。 (或者在功能组件上使用react.memo)
问题 #2 - 分配垃圾(请原谅我这么强硬的词,但这是真的)
onSelectionFinish={selected => {
console.time("EEEE");
console.log("Selection Started");
onSelect(selected);
}}
ignoreList={[".name-col", ".h-col", ".s-col", ".u-col", ".v-col"]}
在这里,在每个渲染上创建一个新数组和新处理程序。这将增加内存使用和垃圾收集器工作。通常,这不是问题,但如果您的组件经常渲染,这将成为问题。或者,如果您像在 getRows() 中那样分配大量对象。值得一提的是,收集垃圾将完全停止您的应用程序。
如何解决: - 尽可能预先计算所有内容并使用结果而不是即时计算。这将为您节省很多 CPU 时间。
希望对您有所帮助!
更新
正如@YashJoshi 提到的,
windowing technique may help here greatly
要获取更多信息,请查看here
@Drag13 @YashJoshi,
我完全同意 windowing 在这里会有帮助,但我们也应该考虑到更改单元格不应重新渲染整个 table(当前 window)或任何其他页面上的组件
@SonuBamniya 应该考虑使用 React Context / redux / 任何其他捕获单元格更改的状态管理库,而不是使用父组件来捕获状态。此外,将每个单元格划分为更新上下文的单独组件,然后您可以在表单组件中使用该上下文而不是整个父组件。
这将大大减少重新渲染。
我有一个table,它的单元格是selectable(使用react-selectable-fast)。有 15000 行,每行有 30 个 Selectable 单元格。 我在重新渲染时遇到了一个奇怪的问题。 重新渲染组件需要更长的时间。
这里是代码沙箱 link 同样的:https://codesandbox.io/s/unruffled-vaughan-vnox7
问题是,当我删除 setState
方法时,它会按预期工作,因为没有重新渲染。但是,由于需要在父组件中捕获单元格上的事件以在页面的其他形式中进行更改(在代码沙箱中不可用),所以我需要更新组件的状态,然后页面是显示所选单元格的时间比平时长。
故障可以在这里看到:https://vnox7.csb.app/
我这里有什么地方做错了吗?
React designed to be fast but can become very slow.
为什么?
问题 #1 - 在任何地方使用函数组件。
函数组件的问题是它在每次父渲染时渲染。因此,如果您在功能组件内部进行了一些繁重的计算(并且您有),您将陷入困境。即使DOM重新计算后不会更新(因为影子DOM检查),你仍然需要计算所有内容以进行比较。
看这里:
const getRows = number => {
*heavy calculation*
}
这里:
<table border="1">
<thead>
<tr>{getHead()}</tr>
</thead>
<tbody>{getRows(1500)}</tbody>
</table>
每个渲染你做同样的计算,你实际上并不需要。
如何修复: - 将功能组件更改为纯组件。 (或者在功能组件上使用react.memo)
问题 #2 - 分配垃圾(请原谅我这么强硬的词,但这是真的)
onSelectionFinish={selected => {
console.time("EEEE");
console.log("Selection Started");
onSelect(selected);
}}
ignoreList={[".name-col", ".h-col", ".s-col", ".u-col", ".v-col"]}
在这里,在每个渲染上创建一个新数组和新处理程序。这将增加内存使用和垃圾收集器工作。通常,这不是问题,但如果您的组件经常渲染,这将成为问题。或者,如果您像在 getRows() 中那样分配大量对象。值得一提的是,收集垃圾将完全停止您的应用程序。
如何解决: - 尽可能预先计算所有内容并使用结果而不是即时计算。这将为您节省很多 CPU 时间。
希望对您有所帮助!
更新 正如@YashJoshi 提到的,
windowing technique may help here greatly
要获取更多信息,请查看here
@Drag13 @YashJoshi,
我完全同意 windowing 在这里会有帮助,但我们也应该考虑到更改单元格不应重新渲染整个 table(当前 window)或任何其他页面上的组件
@SonuBamniya 应该考虑使用 React Context / redux / 任何其他捕获单元格更改的状态管理库,而不是使用父组件来捕获状态。此外,将每个单元格划分为更新上下文的单独组件,然后您可以在表单组件中使用该上下文而不是整个父组件。
这将大大减少重新渲染。