乐趣区

关于前端:如何方便的检测React项目的性能

大家好,我卡颂。

对于长期迭代的 React 我的项目,性能是不能漠视的问题。通常咱们通过:

  • React-Dev-ToolsProfiler 面板
  • 一些第三方工具,比方 why-did-you-render

检测运行时性能瓶颈。

实际上,React自身就内置了性能检测组件 —— Profiler,能够很不便的检测 React 我的项目的性能。

欢送退出人类高质量前端交换群,带飞

应用形式

Profiler是个内置组件,用他包裹须要检测性能的组件即可:

<Profiler id="App" onRender={onRender}>
  <App />
</Profiler>

嵌套应用也是能够的:

<App>
  <Profiler id="Sidebar" onRender={onRender}>
    <Sidebar />
  </Profiler>
  <Profiler id="Content" onRender={onRender}>
    <Content />
  </Profiler>
</App>

Profiler会检测被他包裹的组件树的性能,检测后果会作为 onRender 回调的参数:

function onRender(
  id, 
  phase, 
  actualDuration, baseDuration, 
  startTime, commitTime
) {// ... 回调}

那么,这些参数都是什么意思呢?其实咱们齐全没必要记这些。

咱们只须要晓得,一些典型的性能优化场景该应用哪些参数就行。

场景 1:组件是不是嵌套更新?

对于个别的组件更新,会经验 4 个步骤:

  1. 组件触发更新
  2. 计算更新的影响
  3. 执行 DOM 操作
  4. 视图更新

但如果在上一次更新流程的 4 个步骤还未走完的状况下,又触发新的更新:

能够发现,在这种状况下,视图更新 的机会远滞后于个别更新流程,这会造成页面交互卡顿。

这就是 组件嵌套更新 ,通常咱们在useLayoutEffect 中触发新的更新会遇到这种状况。

Profiler onRender回调的 phase 参数,用来示意组件所处更新阶段:

  • mount,代表组件是首屏渲染
  • update,代表组件更新
  • nested-update,代表组件嵌套更新

通过该参数能够判断组件是否处于嵌套更新。

当遇到嵌套更新造成的性能问题,能够思考用 useEffect 代替useLayoutEffect

场景 2:性能优化到底起没起作用?

当提到 性能优化,很多同学第一反馈就是:

  • useCallback
  • useMemo
  • React.memo

但当咱们应用这些性能优化 API 后,咱们怎么晓得性能是否变得更好?

为了检测优化成果,通常会在要害组件打印个 log,如果状态更新后log 没打印,代表组件没有render,命中缓存胜利,比方这样:

function App() {console.log('App render')
  // ... 省略逻辑
}

但这样并不能反映性能优化的整体成果。这时候能够思考 Profiler 中的 actualDurationbaseDuration参数:

  • baseDuration掂量组件子树在不命中任何缓存时,残缺 render 一次所花工夫
  • actualDuration掂量组件子树理论残缺 render 一次所花工夫

所以,用 baseDuration 减去 actualDuration 残余的工夫,就是性能优化节约的工夫。

比方,对上面的 <App/> 组件性能优化后,只须要在 onRender 中比拟 baseDurationactualDuration之间的差就能度量 <App /> 的性能优化成果:

<Profiler id="App" onRender={onRender}>
  <App />
</Profiler>

这个值越高,代表性能优化成果越好。当靠近 0 时,代表性能优化没有起到作用。

须要留神的是,baseDuration是通过子树中每个组件最近 render 所需工夫汇总求和失去的近似值,有时并不精确

如果你的共事执著的认为所有函数 props 都必须用 useCallback 包裹,所有变量 props 都必须用 useMemo 包裹,请用以上数据狠狠的和他讲道理。

场景 3:我的项目的性能瓶颈在哪?

当咱们要做性能优化时,首先应该明确 —— 我的项目的性能瓶颈在哪?此时,能够用 Profiler 划分几个 待比拟区域,再别离比照actualDuration

比方,对于上面的利用:

<App>
  <Sidebar />
  <Content />
</App>

<Sidebar /><Content />render更耗时?

此时能够用 Profiler 别离包裹这两个组件:

<App>
  <Profiler id="Sidebar" onRender={onRender}>
    <Sidebar />
  </Profiler>
  <Profiler id="Content" onRender={onRender}>
    <Content />
  </Profiler>
</App>

再别离在 onRender 中掂量 actualDuration,值比拟高的区域render 更耗时。

这种形式定制性比拟高。如果想更直观比拟哪些组件 render 更耗时,能够应用 React Dev ToolsProfiler面板的火炬图。

总结

ProfilerReact 内置的性能剖析组件,用于度量其包裹的子树的渲染性能。

最初说个有意思的细节 —— 在官网 Profiler 局部中只介绍了 ProfileronRender这个回调。

Profiler 源码看,他还存在:

  • onCommit回调
  • onPostCommit回调
  • onNestedUpdateScheduled回调

不晓得为什么,他们没有在文档中提及。

退出移动版