React渲染优化
React渲染
优化机会
正如我们在上一篇博文中所看到的,渲染是React判断是否需要在DOM中进行更改的方式,但在某些情况下,在渲染阶段执行的工作和计算可能是一种浪费。毕竟,如果组件的渲染输出是相同的,就不会有DOM更新,因此这项工作是不必要的。
渲染输出应该始终基于当前的props
和state
的组合,因此只要其props
和state
保持不变,就可以提前知道组件的渲染输出是否相同。这是优化React渲染的关键观察点,因为它依赖于我们的代码做更少的工作,并在可能的情况下跳过组件渲染。
优化技术
React提供了一些API,允许我们优化渲染过程:
shouldComponentUpdate
(类组件):生命周期方法,在渲染之前调用,返回一个布尔值(false
表示跳过渲染,true
表示按照通常的方式继续)。逻辑可以根据需要进行变化,但最常见的情况是检查组件的props
和state
是否发生了变化。React.PureComponent
(类组件):基类,在其shouldComponentUpdate
生命周期方法中实现了先前描述的props
和state
变化检查。React.memo()
(任何组件):高阶组件(HOC),包装任何给定的组件。它实现了与React.PureComponent
相同类型的功能,但也可以包装函数组件。
所有这些技术都使用浅相等进行比较。跳过渲染组件意味着跳过默认的递归行为,即跳过整个组件子树的渲染。
参考记忆化
将新的引用作为props
传递给子组件通常并不重要,因为当父组件发生变化时,子组件将重新渲染。然而,如果你试图通过检查其props
是否发生变化来优化子组件的渲染,传递新的引用将导致重新渲染。如果新的引用是更新的数据,这种行为是可以接受的,但如果是父组件传递下来的相同回调函数的新引用,这就是一个问题。
在类组件中,这个问题不太严重,因为它们有实例方法,其引用不会改变,尽管传递给组件子级的任何生成的回调函数都可能导致新的引用。就函数组件而言,React提供了useMemo()
钩子来进行值的记忆化,还提供了useCallback()
钩子专门用于记忆化回调函数。
useMemo()
和useCallback()
可以提供性能优势,但与任何其他记忆化使用方式一样,重要的是要考虑它们的必要性以及它们在长期内提供的净收益。一个好的经验法则是考虑在经常使用相同props
重新渲染的纯函数组件中使用它们,并且可能进行大量计算,而在其他地方避免使用它们。
性能测量
React Developer Tools提供了一个方便的Profiler选项卡,可以可视化和探索React应用程序的渲染过程。在这个选项卡下,你会找到一个设置图标,允许你“在组件渲染时突出显示更新”,以及“在分析时记录每个组件为什么渲染” - 我强烈建议选中这两个选项。记录网站的初始渲染和重新渲染可以提供有关应用程序的瓶颈和问题的宝贵见解,还可以突出显示优化机会(通常使用上面描述的技术之一)。
最后,请记住,React的开发构建比生产构建要慢得多,因此以开发中的绝对时间为度量标准并不是一个有价值的指标。你应该关注的是识别不必要的渲染、记忆化和优化机会,以及潜在的瓶颈。