7 个优化 React 应用的技巧
React 是一个出色的 JavaScript 库,用于构建用户界面,非常适合开发各种规模的应用程序,旨在实现当今 Web 平台上的最高性能。但有时,作为开发人员,我们最终会做出一些导致应用程序性能低下的事情。
在这篇文章中,我汇总了一些专门针对 React 的最佳实践,旨在提升运行时性能。此外,这些实践也适用于 React 以外的领域。我撰写这篇文章的目的仅在于列出这些最佳实践,避免进行详细的解释,以保持文章简洁明了。
识别性能问题
在 react-dom 16.5 及更高版本中,React 团队通过 React DevTools 在开发模式下提供了更强大的性能分析功能。这始终是我在研究潜在的性能优化方案时首先使用的工具。您可以在这里找到它们:
-
React DevTools 分析器。
-
React DevTools 更新高亮显示
- React 维护着一个虚拟 DOM,它会根据 props 或 state 的变化来判断 UI 的哪些部分需要重新渲染。这固然很好,但也意味着我们无法确切地知道应用程序的哪些部分在任何给定时间正在更新。在 React DevTools 中,有一个设置可以启用,它会在元素渲染(或重新渲染)时以视觉方式高亮显示屏幕上的元素。
3. 你为什么要渲染
- 有时,你知道某个 React 组件不应该被重新渲染,除非有非常充分的理由。但如何才能找到这个理由呢?让我来分享一个非常棒的工具:@welldone-software/why-did-you-render。它可以通知你重新渲染的原因。配置完成后,你的控制台会显示相关信息,帮助你追踪特定组件何时以及为何重新渲染。
4. 应更新组件
- ` shouldComponentUpdate ()`方法是 React 中第一个真正意义上的生命周期优化方法。我们可以查看当前和新增的 props 和 state,并决定是否继续执行后续操作。
- `shouldComponentUpdate` 的作用是指示是否应该调用 `render` 方法。在你的例子中,某个父组件已经渲染完成,并且指示它也需要渲染你的子组件实例。
- shouldComponentUpdate 是您终止渲染并表示“不必费心,这里什么都没变”的机会。
- 现在回答你的问题,“既然没有任何变化,为什么还要调用它?” React 本身并不会比较新旧 props。你可以使用 mixin 来帮你完成这项工作(例如 PureRenderMixin),但默认情况下,React 只会让渲染过程继续运行。
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
}
- React 不自行进行比较的原因有二。首先,与分析 props 和 state 相比,跳过渲染带来的性能提升可能微乎其微。由于 React 的渲染机制已经过优化,避免了不必要的 DOM 操作,因此它可以假定组件需要更新,并期望获得合理的性能。其次,进行比较并非易事。你的 prop 是原始类型?不可变对象?数组?复杂对象?是否需要进行深度比较?
- React 的模型是“默认情况下,我们会渲染所有请求的组件。如果您希望为了提升性能而选择不渲染某些组件,请通过实现 shouldComponentUpdate 来告诉我们”。
5. React.PureComponent
- 当一个类组件继承自React.PureComponent基类时,React 会将该组件视为纯组件。React.Component 类和 React.PureComponent 的主要区别在于 shouldComponentUpdate() 方法的实现。在 React.Component 中,shouldComponentUpdate() 方法始终返回 true,而在 React.PureComponent 中,它会将当前状态和属性与新的状态和属性进行比较。
import React, {PureComponent} from ‘react’;
export default class Test extends PureComponent{
render(){
return ‘’;
}
}
- 但关键在于 React.PureComponent 的 shouldComponentUpdate() 方法只能对对象进行浅层比较。如果对象包含复杂的数据结构,它可能会漏掉更深层次的差异。因此,只有在预期 props 和 state 都比较简单时才应该扩展 PureComponent,或者在确定深层数据结构发生变化时使用 forceUpdate() 方法。此外,还可以考虑使用不可变对象来快速比较嵌套数据。
- 此外,React.PureComponent 的 shouldComponentUpdate() 方法会跳过整个组件子树的 prop 更新。请确保所有子组件也都是“纯组件”。
6. React.memo
- 如果使用函数组件而不是基于类的组件,React.memo 可以提供类似的功能。
- React.memo 是一个高阶组件。
- 如果你的组件在给定相同 props 的情况下渲染出相同的结果,你可以将其包裹在 React.memo 调用中,通过缓存结果来提升性能。这意味着 React 将跳过组件的渲染,直接复用上次渲染的结果。
- React.memo 只检查 prop 的变化。如果你的函数组件被 React.memo 包裹,并且在其实现中使用了 useState 或 useContext Hook,那么当 state 或 context 发生变化时,它仍然会重新渲染。
- 默认情况下,它只会对 props 对象中的复杂对象进行浅比较。如果您想要控制比较过程,还可以将自定义比较函数作为第二个参数提供。
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);
7. 使用 react-window 虚拟化大型列表
- 有时您可能需要显示包含大量行的大型表格或列表。加载此类列表中的每个项目可能会显著影响性能。
- 列表虚拟化,或称“窗口化”,是指只渲染用户可见的内容。初始渲染的元素数量只是整个列表的一小部分,当用户继续滚动时,可见内容的“窗口”也会随之移动。这可以同时提升列表的渲染和滚动性能。
- react-window是一个可以高效渲染大型列表的库。这里有一篇关于这个主题的精彩文章。
(附注:如果您对效率、科技和产品话题感兴趣,请在Twitter上关注我,我会以轻松有趣的方式分享这些内容。)
文章来源:https://dev.to/palaklive/7-hacks-to-optimize-your-react-app-27e6

