发布于 2026-01-06 1 阅读
0

优化 React 项目性能的几个技巧

优化 React 项目性能的几个技巧

本文将介绍几种优化 React 应用性能的方法,并通过示例演示如何在进行优化时选择合适的解决方案。

在开始之前,我们先来看一个例子。
这里有24个复选框,供用户选择他们想要的时间。

这个例子的问题在于,每次用户点击复选框时,所有复选框都会重新渲染。

那么,我们该如何解决这个问题呢?

React DevTools 分析器

在开始优化应用程序之前,我们需要知道如何识别应用程序的性能问题?
react-dom 16.5+ 和 react-native 0.57+ 通过React DevTools Profiler提供了增强的性能分析功能。

使用 React DevTools Profiler 非常简单,只需点击左上角的录制按钮,与应用程序交互,然后再次点击该按钮停止录制即可。这样我们就能获得结果,从而识别问题所在。
react-profiler

当我们通过 React DevTools Profiler 检查火焰图时,可以看到不必要的重新渲染。
react-profiler

既然我们知道了问题所在,那就让我们尝试几种不同的解决方法吧。

纯组分

首先,我们可以尝试最简单的解决方案——PureComponent 我们只需要将类继承从component更改为PureComponent,然后React会为我们完成剩下的工作。

// before
export default class CheckBox extends React.Component {
  ...
}

// after
export default class CheckBox extends React.PureComponent {
  ...
}
Enter fullscreen mode Exit fullscreen mode

但是,即使我们进行了更改PureComponent,也发现并没有阻止不必要的重新渲染。原因是每次都会创建一个新的 handleToggle 函数。因此,即使我们应用了PureComponent该函数,当 App 组件重新渲染时,所有 CheckBox 组件仍然会被重新渲染。

应组件更新

因为PureComponent之前的方法行不通。所以现在我们需要自己进行检查。我们可以使用ShouldComponentUpdate来阻止不必要的渲染。

shouldComponentUpdate(nextProps) {
  const {value, isChecked} = nextProps;
  return this.props.value !== value || this.props.isChecked !== isChecked
}
Enter fullscreen mode Exit fullscreen mode

现在,当我们再次检查 React DevTools Profiler 时,我们会发现只有点击复选框会重新渲染。
react-profiler

React.memo

如果我们想使用函数组件而不是类组件,还有另一种选择——React.memo
React.memo它会执行与 `<class>` 相同的检查PureComponent。但它允许我们传递第二个参数来进行自定义检查,类似于 `<class>` ShouldComponentUpdate。但我们需要注意的是,它的返回值应该与 `<class>` 相反ShouldComponentUpdate

export default React.memo(CheckBox, (prevProps, nextProps) => {
  return prevProps.value === nextProps.value && prevProps.isChecked === nextProps.isChecked
});
Enter fullscreen mode Exit fullscreen mode

使用备忘录

函数组件的另一种解决方案是使用钩子 - useMemo

export default function CheckBox ({value, isChecked, handleToggle}){
  return React.useMemo(() => {
    return (
      <div>
        <label>
          <input type="checkbox" value={value} checked={isChecked} onChange={handleToggle} />
          {value}
        </label>
      </div>
    )
  }, [value, isChecked]);
}
Enter fullscreen mode Exit fullscreen mode

虽然这可以帮助我们避免不必要的重新渲染,但我们会看到来自eslint-plugin-react-hooks的错误。
eslint-react-hooks

eslint会提醒我们将其添加handleToggle到依赖项数组中。但我们不能这样做,因为为了防止不必要的重新渲染,我们必须忽略它。我们可以轻松地使用 `resolve`eslint-disable来避免此错误。但实际上,此错误消息确实指出了一个重要的问题。

尽管上述大多数解决方案(除了[此处应填写PureComponent具体方法名称])可以帮助我们优化性能,但这些自定义逻辑也会增加代码的维护难度,并可能引入一些潜在的错误。
例如,当其他团队成员为复选框组件添加一个新的属性时,如果他/她忘记在[此处应填写具体方法名称]或[此处应填写具体方法名称]isDarkMode中调整自定义逻辑,那么深色模式将无法正常工作,因为复选框组件在属性更改时不会重新渲染。ShouldComponentUpdateReact.memoisDarkMode

那么,我们该如何解决这个问题呢?

使用回调

解决这个性能问题的更好方法是避免handleToggle每次都创建新函数。
我们可以将 App 组件改为类组件,或者使用另一个钩子函数useCallback来完成这项工作。

const handleToggle = useCallback(targetTime => {
  setTimeCheckboxes(timeCheckBoxes => {
    return timeCheckBoxes.map(({ time, isChecked }) => ({
      time,
      isChecked: targetTime === time ? !isChecked : isChecked
    }));
  });
}, []);
Enter fullscreen mode Exit fullscreen mode

因为我们toggle现在无需每次都创建新函数,只需应用于PureComponent复选框即可。这样,我们就能在不向代码库添加任何自定义逻辑的情况下,避免不必要的重新渲染。

测量

此外,我们不仅需要了解如何优化我们的应用程序,还需要知道如何衡量我们应用程序的性能。

React Profiler

React 提供了一个组件来帮助我们实现这一点——Profiler 只需
将我们的 App 组件包裹在 Profiler 中Profiler,即可获取所需的信息。

<Profiler id="app" onRender={onRenderCallback}>
  <div className="App">
    ...
  </div>
</Profiler>
Enter fullscreen mode Exit fullscreen mode

onRenderprop 会将这些信息传递给我们的回调函数。这样我们就可以打印出所需的信息了。

function onRenderCallback(
  id, // the "id" prop of the Profiler tree that has just committed
  phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
  actualDuration, // time spent rendering the committed update
  baseDuration, // estimated time to render the entire subtree without memoization
  startTime, // when React began rendering this update
  commitTime, // when React committed this update
  interactions // the Set of interactions belonging to this update
) {
  // Aggregate or log render timings...
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以知道优化前后的区别了。


  • 分析器

  • 之后(应用useCallbackPureComponent
    分析器

Chrome 开发者工具:性能

另一种方法是使用 Chrome 开发者工具。我们可以选择Performance标签页并开始录制,就像我们在 React DevTools Profiler 中所做的那样。
(这里我降低了 CPU 使用率,以便更容易地识别性能问题;如果需要,我们也可以模拟较慢的网络。)
chrome-dev-tools

然后我们可以看到这样的结果。

  • 之前:152.72毫秒,132.22毫秒,204.83毫秒chrome-dev-tools
  • (应用useCallbackPureComponent)之后:15.64毫秒,18.10毫秒,12.32毫秒chrome-dev-tools

结论

React 提供了许多 API 和工具来帮助我们优化应用程序。在优化性能的同时,我们需要谨慎选择解决方案,以确保在提升应用程序性能后,代码仍然易于维护。

--

参考

文章来源:https://dev.to/oahehc/few-tips-to-optimizing-performance-of-react-project-5h25