作为“性能优化”的手段。一般使用useMemo缓存函数组件中消耗性能的计算结果:functionApp(){constmemoizedValue=useMemo(()=>computeExpensiveValue(a,b),[a,b]);//...}的newmemoizedValue只会在依赖关系发生变化后重新计算。你有没有想过如果使用useMemo来缓存函数组件的返回值会发生什么?例如,我们有一个全局上下文——AppContext。由于同学们的懒惰,随着项目的迭代,新添加的context都选择放在AppContext中,导致AppContext中包含的内容越来越多。现在我们有一个Tree组件,它将渲染ExpensiveTree,这是一个消耗大量性能的大型组件。functionTree(){letappContextValue=useContext(AppContext);lettheme=appContextValue.theme;return;}该组件在内部依赖于AppContext中的主题状态。由于AppContext中包含了很多与主题无关的状态,每次更新其他不相关的状态时,Tree都会重新渲染,ExpensiveTree组件也会重新渲染。既然这个优化任务已经掌握在你手中,你应该怎么做呢?在优化ExpensiveTree时,useMemo可以派上用场:functionTree(){letappContextValue=useContext(AppContext);lettheme=appContextValue.theme;returnuseMemo(()=>{return;},[theme])我们使用返回的ExpensiveTree作为useMemo的返回值,theme作为依赖。这样即使AppContext改变,Tree反复渲染,ExpensiveTree也只会在主题改变后才渲染。原理分析要理解为什么这样有效,你需要知道三件事:useMemo的返回值是多少?函数组件的返回值是什么?React组件什么时候渲染?返回值保存在组件对应的fiber中,只有当依赖(第二个参数)发生变化时,才会再次调用第一个参数(函数)计算一个新的值。回答第二个问题:函数组件的返回值是一个JSX对象。多次调用同一个功能组件,返回多个“不同”的JSX对象(即使props没变,JSX也是一个新的引用)。根据上面两个回答,我们可以得出一个结论:上面useMemo的用法其实是在函数组件对应的fiber中缓存了一个完整的JSX对象。第三个问题,功能组件需要同时满足以下条件才能不渲染:1oldProps===NewProps前后两次更新props是全等的,注意“全等”。2组件上下文没有变化3workInProgress.type===current.type组件更新前后fiber.type没有变化,比如div没有变成p。4!includesSomeLane(renderLanes,updateLanes)当前fiber上没有更新,或者有更新但优先级低。更详细的解释可以参考这篇文章:React组件什么时候渲染?当我们不使用useMemo包装返回值时,每个Tree渲染都会返回一个全新的JSX对象。所以对于ExpensiveTree,oldProps!==newProps。再看2:ExpensiveTree的内部上下文没有变化,如果满意再看3:ExpensiveTree更新前后,类型都是ExpensiveTree,如果满意再看4:没有ExpensiveTree中状态更新,如果满意,那么当我们用useMemo包裹ExpensiveTree时,在主题不变的情况下,每次Treerender后返回的都是同一个JSX对象,满足了第一个需求。因此,ExpensiveTree不会渲染。综上所述,本文提到的useMemo的用法并没有体现在官网文档中,而是Dan在#15156[1]中介绍的。与Vue相比,React更加灵活,需要开发者在开发过程中更加注重细节。要想完全理解React,你可能需要学习一些源码层面的知识。参考[1]#15156:https://github.com/facebook/react/issues/15156#issuecomment-474590693