备忘之前()

2021-02-27 10:38:01

有很多关于React性能优化的文章。通常,如果某些状态更新很慢,则需要:

确认您正在运行生产版本。 (在极端情况下,开发构建有意地变慢,甚至降低了一个数量级。)

确认您没有将状态放在树上的位置超过必要的位置。 (例如,将输入状态放置在集中存储中可能不是最好的主意。)

运行React DevTools Profiler来查看要重新渲染的内容,并使用memo()包装最昂贵的子树。 (并在需要的地方添加useMemo()。)

最后一步很烦人,尤其是对于介于两者之间的组件而言,理想情况是编译器会为您完成。将来可能会。

在本文中,我想分享两种不同的技术。它们非常基础,这就是为什么人们很少意识到他们可以改善渲染性能的原因。

这些技术是对您已经知道的知识的补充!它们不会取代备忘或useMemo,但通常最好先尝试一下。

从' react'导入{useState} ;导出默认功能App(){让[color,setColor] = useState(' red');返回(您好,世界!); }函数ExpensiveTree(){现在=性能。现在 ( ) ; while(performance。now()-now< 100){//人工延迟-100ms不做任何操作} return我是一个非常慢的组件树。 ; }

问题在于,只要App内部的颜色发生变化,我们都会重新渲染< ExpensiveTree />我们人为地延迟了很慢的时间。

我可以在上面放memo()并命名为一天,但是已有很多关于它的文章,所以我不会花时间在上面。我想展示两种不同的解决方案。

如果您仔细查看渲染代码,您会发现返回的树中只有一部分实际上在乎当前颜色:

导出默认功能App(){让[color,setColor] = useState(' red');返回(您好,世界!); }

因此,让我们将该部分提取到一个Form组件中,然后将状态下移到其中:

导出默认函数App(){return(); }函数Form(){让[color,setColor] = useState(' red');返回(您好,世界!); }

如果在昂贵的树上方某处使用了状态块,则上述解决方案将无法正常工作。例如,假设我们将颜色放在父级< div>上:

导出默认功能App(){让[color,setColor] = useState(' red');返回(您好,世界!); }

现在看来,我们不能仅仅将不使用颜色的部分“提取”到另一个组件中,因为它将包括父级< div&gt ;,然后将其包含< ExpensiveTree />。这次无法避免备忘,对不对?

导出默认功能App(){return(Hello,world!); }函数ColorPicker({children}){let [color,setColor] = useState(" red");返回({children}); }

我们将App组件一分为二。依赖颜色的部分以及颜色状态变量本身已移至ColorPicker。

无关颜色的部分保留在App组件中,并以JSX内容(也称为儿童道具)的形式传递给ColorPicker。

当颜色改变时,ColorPicker重新渲染。但是它仍然具有上次从App获得的子道具,因此React不会访问该子树。

在应用诸如memo或useMemo之类的优化之前,先看一下是否可以将变化的部分与不变的部分分开。

这些方法的有趣之处在于,它们实际上与性能没有任何关系。使用children props拆分组件通常可以使应用程序的数据流更易于跟踪,并减少了遍历整个树的props的数量。在这种情况下,性能的提高是重中之重,而不是最终目标。

例如,当服务器组件稳定并准备好采用时,我们的ColorPicker组件可以从服务器接收其子代。整个< ExpensiveTree />组件或其部件可以在服务器上运行,甚至顶级的React状态更新也将“跳过”客户端上的那些部件。

连备忘录都做不到!但是,这两种方法都是互补的。不要忽略向下移动状态(并向上移动内容!)

然后,在不够的地方,使用探查器并撒上那些备忘录。

这不是一个新的想法。这是React组成模型的自然结果。很简单,它没有被重视,应该得到更多的爱。