R

React:使用 React.memo、useMemo 和 useCallback 优化组件 GenAI LIVE!| 2025 年 6 月 4 日

2025-06-08

React:使用 React.memo、useMemo 和 useCallback 优化组件

GenAI LIVE! | 2025年6月4日

本文最初发布于Headway 博客。访问headway.io,了解我们如何引领潮流。🏄‍♀️

大多数情况下,React 的性能并非您所需。核心库在后台做了大量工作,以确保所有内容都能高效渲染。然而,偶尔您可能会遇到组件渲染频率过高的情况,从而导致网站速度变慢。

让我们看一个例子:

const ListPage = ({data, title}) => (  
  <div>  
    <Header title={title}/>  
    <List listItems={data}/>  
  </div>  
)  

在这个例子中,任何对 的更改data都会导致ListPage重新渲染其所有子组件,包括Header组件本身,即使它title本身没有变化。Header在相同的 props 下, 会渲染相同的结果,因此任何使用相同 props 的渲染都是不必要的。在这种情况下,这可能不是什么大问题,但如果<Header/>每次渲染时都要执行一些昂贵的计算,我们希望确保它只在必要时进行渲染。

值得庆幸的是,有几种方法可以针对这种情况进行优化。

使用基于类的组件时,PureComponent如果传入的 props 相同,则返回最后渲染的值。此外,还有一个shouldComponentUpdate函数可以进行更精细的控制。使用函数式组件时,React 提供了三种优化方法,本文将重点介绍它们:React.memouseMemouseCallback

两个男人在电脑屏幕上讨论 React 项目

React.Memo

React.memo是一个高阶组件,用于记忆函数组件的结果。如果一个组件在给定相同 props 的情况下返回相同的结果,那么将其包装进去memo可以提升性能。以我们<Header/>之前的例子为例。

假设它看起来像这样:

const Header = ({title}) => <h1>{title}</h1>

export default Header;  

我们可以看到,除非发生变化,否则不需要渲染此组件title,因此将其包装在内是安全的React.memo


const Header = ({title}) => <h1>{title}</h1>

export default React.memo(Header);  

现在,无论何时Header渲染,它都会对其 props 进行浅层比较。如果这些 props 相同,它将跳过渲染,而是返回上次渲染的值。

关于使用 React Dev Tools 的简要说明memo。在撰写本文时,组件的包装方式如下……


const Header = React.memo(({title}) => <h1>{title}</h1>));

export default Header;  

…会导致你的组件像在 React Dev Tools 中一样显示Unknown。要解决这个问题,请在定义组件后将其包裹起来memo,就像我们之前做的那样:


const Header = ({title}) => <h1>{title}</h1>;

export default React.memo(Header);  

使用备忘录

useMemo允许您记住函数的结果,并返回该结果直到依赖项数组发生变化。

例如:

const widgetList = useMemo(  
  () => widgets.map(  
    w => ({  
      ...w,  
      totalPrice: someComplexFunction(w.price),  
      estimatedDeliveryDate: someOtherComplexFunction(w.warehouseAddress)  
    }),  
  ),  
  [widgets],  
);  

在这个例子中,一个组件接收一个 Widget 列表。传入组件的 Widget 需要进行映射,以包含总价和预计送达日期,这需要使用一些复杂且开销很大的函数。如果该组件渲染成功,并且 的值widgets保持不变,则无需再次运行这些开销很大的函数。

使用useMemo将记住结果,因此如果widgets自组件上次渲染以来没有改变,它将跳过函数调用并返回最后得到的结果。

使用回调

useCallback可以防止父子组件之间不必要的渲染。

举个例子:

const Parent = () => {  
  const [showExtraDetails, setShowExtraDetails] = useState(false);  
  return (  
    [...]  
    <Child onClick={() => { showData(showExtraDetails); }/>  
    [...]  
  );  
}  

这个组件每次执行操作时都会导致Child重新渲染Parent,即使ChildPureComponent或者包裹在中React.memo,因为onClick每次渲染都会不同。useCallback可以像这样处理这种情况:


const Parent = () => {  
  const [showExtraDetails, setShowExtraDetails] = useState(false);  
  const handleClick = useCallback(  
    () => {  
    showData(showExtraDetails);  
  },  
  [showExtraDetails],  
);  
  return (  
    [...]  
    <Child onClick={() => {handleClick}/>  
    [...]  
  );  
}  

现在handleClick将具有相同的值直到showExtraDetails发生变化,这将减少渲染的次数Child

React 优化中需要考虑的事项

关于过早优化,需要注意的是。React 通常足够快,无需借助任何这些技术即可处理大多数用例。我建议您首先在不进行任何优化的情况下构建组件,并仅在必要时考虑添加性能增强。

了解更多资源

如果您想进一步探索这些 API,这里有一些资源可以帮助您更好地理解。

React.memo

使用备忘录

使用回调

如果您希望进一步优化您的 React 应用程序,React 文档包含有关性能的重要部分。

鏂囩珷鏉ユ簮锛�https://dev.to/headwayio/react-optimize-components-with-react-memo-usememo-and-usecallback-39h8
PREV
Web 缓存简介(含 Python 示例)Django 怎么样?最难的部分
NEXT
使用缓存来提升 PHP 项目