分析 React 组件的渲染性能
今天,我们介绍一下如何使用 React Profiler API
分析 React
组件的渲染性能。
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
组件各部分的渲染时间:
Profiler
的 onRender
回调接收描述渲染内容和所花费时间的参数:
-
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 Vaughn
, React
通过新的调度器包中的交互跟踪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 DevTools
的 Lighthouse
面板中:
Next.js
的最新版本还为许多事件添加了更多的用户计时标记和度量,包括:
-
Next.js-hydration
-
Next.js-nav-to-render
所有这些度量都显示在 Timing
区域中:
DevTools & Lighthouse
Lighthouse
和 Chrome DevTools Performance
面板可用于深入分析 React
应用程序的负载和运行时性能,突出显示以用户为中心的关键指标:
React
用户可能会喜欢像总阻塞时间(TBT)这样的新指标,它量化了一个页面在变得具有可靠交互性之前的非交互性(变为交互性的时间)。下面我们可以看到一个应用程序的并发模式的TBT之前/之后的TBT,在此更好地分散更新:
这些工具通常有助于获得一个浏览器级别的瓶颈视图,如延迟交互的长时间任务(如按钮点击响应),如下所示:
Lighthouse
还提供了许多为 React
特殊定制的审计:
译自 https://addyosmani.com/blog/profiling-react-js/
轻点在看,支持作者❤️