在代码中使用记忆化来减少不必要的重新渲染

2025-06-08

在代码中使用记忆化来减少不必要的重新渲染

记忆化是指存储开销较大的函数调用结果,并在需要时重新使用该结果,而不是不必要地重新计算。它并非你每天都需要用到的东西,但如果你正在寻找提升 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} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

默认情况下,当父组件重新渲染时,其所有子组件也会重新渲染。因此,在这种情况下,每次有人点击按钮(并改变 的状态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} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

由于ExpensiveMemovalueprop 始终是100,因此它将在页面最初加载时渲染一次,然后再也不会重新渲染。

如果您使用类组件,使用React.PureComponent将实现相同的效果。

注意你的道具!

在上面的例子中,我们传入的 prop 是一个数字。但是如果我们传入一个对象或数组呢?

<ExpensiveMemo value={['hello', 'world']}/>
Enter fullscreen mode Exit fullscreen mode

这样做会导致Expensive组件始终重新渲染,因为我们每次都会创建一个新数组并将其传入。

另一方面,如果你修改了一个已经存在的数组并将其作为 prop 传入:

const countArray = [];
function App() {
  //...
  countArray.push[count]
  return (
    // ...
    <ExpensiveMemo value={countArray}/>
Enter fullscreen mode Exit fullscreen mode

永远不会导致重新渲染,因为 Reactmemo只对其 props 进行浅比较,这意味着它只关心是否countArray是相同的数组(而不是其中的内容)。

为了解决这个问题,您可以将自己的自定义areEqual函数作为第二个参数传递给React.memo

使用 React.useMemo() 来记忆值

我们不必担心是否会导致ExpensiveMemo重新渲染,而是可以calculatePrimes使用React.useMemo钩子来记忆昂贵的函数:

const Expensive = ({value}) => {
    const primesMemo = useMemo(() => calculatePrimes(value, value), [value]); 
    return <>{primesMemo}</>
}
Enter fullscreen mode Exit fullscreen mode

我们传入的第一个参数useMemo是用来计算所需值的函数。第二个参数是依赖项的数组(依赖项是指那些一旦发生变化就需要重新计算其值的元素),在本例中就是 prop value

现在calculatePrimes只有当发生变化时才会被调用value

如果您正在使用类组件,那么有一些库可以完成与相同的操作useMemo,例如memoize-one

结论

如果您的应用程序相当小或者没有任何昂贵的计算,那么在代码中记忆内容将增加更多代码和复杂性,而不会真正提供任何性能优势,所以我不建议使用它 - 但我认为这绝对是一件好事,特别是当您的应用程序变得越来越大时。

感谢阅读!

鏂囩珷鏉ユ簮锛�https://dev.to/emma/use-memoization-in-your-code-to-reduce-unnecessary-re-renders-4gh2
PREV
您的技术招聘邮件失败的原因案例研究 1:冗长且含糊不清案例研究 2:完全错误的角色案例研究 3:您是完美人选,但您认识其他人吗?结论
NEXT
我为开发人员制作了一个投资组合生成器 - 欢迎反馈!