在代码中使用记忆化来减少不必要的重新渲染
记忆化是指存储开销较大的函数调用结果,并在需要时重新使用该结果,而不是不必要地重新计算。它并非你每天都需要用到的东西,但如果你正在寻找提升 React 应用性能的方法,那么它值得你牢记。
使用火焰图查找问题🔥
在深入研究如何为应用添加记忆功能之前,最好先诊断一下应用性能问题的根源。您可以做的一件事是打开浏览器的“性能”选项卡,记录应用渲染时的性能。
您在这里看到的是 Firefox 浏览器中的火焰图(Chrome 中也会出现类似的效果)。顶部是录制过程中发生事件的完整时间线,深蓝色部分是我选中的时间线部分。所有彩色条形图都表示我们的应用在时间线选中部分中正在发生的事情。
在这个场景中,我在大约 1250 毫秒时按下了 React 应用上的一个按钮,帧率就开始下降(正如顶部下降的绿线所示)。我们可以看到(在屏幕底部)根本原因是组件内部的calculatePrimesExpensive
函数。
代码如下:
const Expensive = ({ value }) => {
const primes = calculatePrimes(value, value) // does a lot of math
return <>{primes}</>
}
const App = () => {
const [count, updateCount] = useState(0)
return (
<>
<button onclick={updateCount(count + 1)}>Click</button>
<Expensive value={100} />
</>
);
}
默认情况下,当父组件重新渲染时,其所有子组件也会重新渲染。因此,在这种情况下,每次有人点击按钮(并改变 的状态App
,导致其重新渲染)时,Expensive
也会重新渲染并调用calculatePrimes
。
Expensive
考虑到我们每次都传递相同的 prop ,calculatePrimes
每次都会返回完全相同的输出。它实际上不需要重新渲染,所以这里有机会记住组件。
使用 React.memo() 来记忆组件
我们可以通过以下方式使组件仅在其 props 发生变化时重新渲染React.memo
:
const ExpensiveMemo = memo(function Expensive({ value }) {
// ...
});
function App() {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>Click</button>
<ExpensiveMemo value={100} />
</>
);
}
由于ExpensiveMemo
的value
prop 始终是100
,因此它将在页面最初加载时渲染一次,然后再也不会重新渲染。
如果您使用类组件,使用React.PureComponent将实现相同的效果。
注意你的道具!
在上面的例子中,我们传入的 prop 是一个数字。但是如果我们传入一个对象或数组呢?
<ExpensiveMemo value={['hello', 'world']}/>
这样做会导致Expensive
组件始终重新渲染,因为我们每次都会创建一个新数组并将其传入。
另一方面,如果你修改了一个已经存在的数组并将其作为 prop 传入:
const countArray = [];
function App() {
//...
countArray.push[count]
return (
// ...
<ExpensiveMemo value={countArray}/>
这永远不会导致重新渲染,因为 Reactmemo
只对其 props 进行浅比较,这意味着它只关心是否countArray
是相同的数组(而不是其中的内容)。
为了解决这个问题,您可以将自己的自定义
areEqual
函数作为第二个参数传递给React.memo
。
使用 React.useMemo() 来记忆值
我们不必担心是否会导致ExpensiveMemo
重新渲染,而是可以calculatePrimes
使用React.useMemo钩子来记忆昂贵的函数:
const Expensive = ({value}) => {
const primesMemo = useMemo(() => calculatePrimes(value, value), [value]);
return <>{primesMemo}</>
}
我们传入的第一个参数useMemo
是用来计算所需值的函数。第二个参数是依赖项的数组(依赖项是指那些一旦发生变化就需要重新计算其值的元素),在本例中就是 prop value
。
现在calculatePrimes
只有当发生变化时才会被调用value
!
如果您正在使用类组件,那么有一些库可以完成与相同的操作
useMemo
,例如memoize-one。
结论
如果您的应用程序相当小或者没有任何昂贵的计算,那么在代码中记忆内容将增加更多代码和复杂性,而不会真正提供任何性能优势,所以我不建议使用它 - 但我认为这绝对是一件好事,特别是当您的应用程序变得越来越大时。
感谢阅读!
鏂囩珷鏉ユ簮锛�https://dev.to/emma/use-memoization-in-your-code-to-reduce-unnecessary-re-renders-4gh2