分析 React 组件的渲染性能

今天,我们介绍一下如何使用 React Profiler API 分析 React 组件的渲染性能。


出于演示目的,我们将使用一个电影排队 APP 。

The React Profiler API

React Profiler API 会分析渲染和渲染成本,以帮助识别应用程序中卡顿的原因。

import React, { Fragment, unstable_Profiler as Profiler} from "react";

Profiler 接受一个 onRender 回调函数,当被分析的渲染树中的组件提交更新时,就会调用它。

const Movies = ({ movies, addToQueue }) => (
  <Fragment>
    <Profiler id="Movies" onRender={callback}>

为了进行测试,让我们尝试使用 Profiler 来测量 Movies 组件各部分的渲染时间:

ProfileronRender 回调接收描述渲染内容和所花费时间的参数:

  • id: 生提交的 Profiler 树的 id。如果有多个 profiler,它能用来分辨树的哪一部分发生了“提交”。

  • phase: "mount" (首次挂载) 或 "update" (重新渲染),判断是组件树的第一次装载引起的重渲染,还是由 props、state 或是 hooks 改变引起的重渲染。

  • actualDuration: 次更新在渲染 Profiler 和它的子代上花费的时间。

  • baseDuration: 在 Profiler 树中最近一次每一个组件 render 的持续时间。这个值估计了最差的渲染时间。

  • startTime: 本次更新中 React 开始渲染的时间戳。

  • commitTime:  本次更新中 React commit 阶段结束的时间戳。在一次 commit 中这个值在所有的 profiler 之间是共享的,可以将它们按需分组。

  • interactions: 当更新被制定时,“interactions” 的集合会被追踪。

const callback = (id, phase, actualTime, baseTime, startTime, commitTime) => {
    console.log(`${id}'s ${phase} phase:`);
    console.log(`Actual time: ${actualTime}`);
    console.log(`Base time: ${baseTime}`);
    console.log(`Start time: ${startTime}`);
    console.log(`Commit time: ${commitTime}`);
}

我们可以加载页面,然后打开 Chrome DevTools 控制台,查看下面的时间:

我们还可以打开 React DevTools ,转到 Profiler 选项卡并可视化我们的组件渲染时间。下面是火焰图视图:

我也喜欢使用排名视图,该视图已排序,因此渲染时间最长的组件显示在顶部:

也可以使用多个 Profiler 来测量应用程序的不同部分:

import React, { Fragment, unstable_Profiler as Profiler} from "react";

render(
  <App>
    <Profiler id="Header" onRender={callback}>
      <Header {...props} />
    </Profiler>
    <Profiler id="Movies" onRender={callback}>
      <Movies {...props} />
    </Profiler>
  </App>
);

但是,如果要追踪交互行为怎么办?

交互跟踪API

如果我们可以追踪交互行为(例如单击UI)来回答比如 “单击此按钮需要多长时间才能更新DOM?” 之类的问题,那就太强大了。感谢 Brian VaughnReact 通过新的调度器包中的交互跟踪API对交互跟踪提供了实验支持。这里有更详细的记录。

交互带有一个注释(例如“单击添加到购物车按钮”)和一个时间戳。还应该为交互提供一个回调函数,你可以在其中执行与交互相关的工作。

在电影APP中,有一个 “将电影添加到队列” 按钮(+)。单击此交互将电影添加到你的观看队列:

以下是此交互的跟踪状态更新的示例:

import { unstable_Profiler as Profiler } from "react";
import { render } from "react-dom";
import { unstable_trace as trace } from "scheduler/tracing";

class MyComponent extends Component {
  addMovieButtonClick = event => {
    trace("Add To Movies Queue click", performance.now(), () => {
      this.setState({ itemAddedToQueue: true });
    });
  };
}

我们可以记录这个交互,并在 React DevTools 中看到它的持续时间:

也可以使用交互跟踪API跟踪首次渲染,如下所示:

import { unstable_trace as trace } from "scheduler/tracing";

trace("initial render", performance.now(), () => {
   ReactDom.render(<App />, document.getElementById("app"));
});

Puppeteer

对于 UI 交互的更深入的脚本跟踪,你可能对 Puppeteer 感兴趣。Puppeteer 是一个 Node 库,它提供了一个高级API,用于通过 DevTools 协议控制无头浏览器。

它提供了了 tracing.start()/stop() 这些工具方法,以捕获 DevTools 工作的性能跟踪。下面,我们使用它来跟踪单击主按钮时发生的情况。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  const navigationPromise = page.waitForNavigation();
  await page.goto('https://react-movies-queue.glitch.me/')
  await page.setViewport({ width: 1276, height: 689 });
  await navigationPromise;

  const addMovieToQueueBtn = 'li:nth-child(3) > .card > .card__info > div > .button';
  await page.waitForSelector(addMovieToQueueBtn);

  // Begin profiling...
  await page.tracing.start({ path: 'profile.json' });
  // Click the button
  await page.click(addMovieToQueueBtn);
  // Stop profliling
  await page.tracing.stop();

  await browser.close();
})();

profile.json 加载到 DevTools Performance 面板中,我们可以通过单击按钮来查看所有由此产生的 JavaScript 函数调用:

User Timing API

通过 User Timing API,可以使用高精度时间戳来测量应用程序的自定义性能指标。window.performance.mark() 存储带有相关名称的时间戳,而 window.performance.measure() 存储两个标记之间经过的时间。

// Record the time before running a task
performance.mark('Movies:updateStart');
// Do some work

// Record the time after running a task
performance.mark('Movies:updateEnd');

// Measure the difference between the start and end of the task
performance.measure('moviesRender', 'Movies:updateStart', 'Movies:updateEnd');

当你使用 Chrome DevTools 性能面板配置一个React应用程序时,你会发现一个名为Timings 的部分,里面存储了 React 组件的处理时间。渲染时, React 可以使用 User Timing API 发布此信息。

注意:React从他们的开发包中删除了 User Timing API ,取而代之的是 React Profiler,它提供了更准确的计时。他们可能会在未来的3级浏览器中重新添加它。

在网上,你会发现一些 React 应用利用 User Timing API 来定义自己的自定义指标。其中包括 Reddit 的“显示第一个帖子标题的时间”和 Spotif y的“准备播放的时间”:

自定义用户计时指标也可以方便地反映在 Chrome DevToolsLighthouse 面板中:

Next.js 的最新版本还为许多事件添加了更多的用户计时标记和度量,包括:

  • Next.js-hydration

  • Next.js-nav-to-render

所有这些度量都显示在 Timing 区域中:

DevTools & Lighthouse

LighthouseChrome DevTools Performance 面板可用于深入分析 React 应用程序的负载和运行时性能,突出显示以用户为中心的关键指标:

React 用户可能会喜欢像总阻塞时间(TBT)这样的新指标,它量化了一个页面在变得具有可靠交互性之前的非交互性(变为交互性的时间)。下面我们可以看到一个应用程序的并发模式的TBT之前/之后的TBT,在此更好地分散更新:

这些工具通常有助于获得一个浏览器级别的瓶颈视图,如延迟交互的长时间任务(如按钮点击响应),如下所示:

Lighthouse 还提供了许多为 React 特殊定制的审计:

译自  https://addyosmani.com/blog/profiling-react-js/

轻点在看,支持作者❤️

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值