作者:ConardLi
转发链接:https://mp.weixin.qq.com/s/tDWBFlZf-rh_IvlSqKTcgg
前言
今天,我们介绍一下如何使用 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 组件各部分的渲染时间:
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 特殊定制的审计:
推荐React 学习相关文章
《这就是你日思夜想的 React 原生动态加载「值得收藏」》
《手把手教你从Mixin深入到HOC再到Hook【React】》
《深入Facebook 官方React 状态管理器Recoil讲解》
《React源码分析与实现(一):组件的初始化与渲染「实践篇」》
《React源码分析与实现(二):状态、属性更新->setState「实践篇」》
《手把手教你10个案例理解React hooks的渲染逻辑「实践」》
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手深入Redux react-redux中间件设计及原理(上)【实践】》
《手把手深入Redux react-redux中间件设计及原理(下)【实践】》
《为了学好 React Hooks, 我解析了 Vue Composition API》
《【React 高级进阶】探索 store 设计、从零实现 react-redux》
《深入浅出掌握React 与 React Native这两个框架》
《你需要的 React + TypeScript 50 条规范和经验》
《手把手教你深入浅出实现Vue3 & React Hooks新UI Modal弹窗》
《全平台(Vue/React/微信小程序)任意角度旋图片裁剪组件》
作者:ConardLi
转发链接:https://mp.weixin.qq.com/s/tDWBFlZf-rh_IvlSqKTcgg
本文暂时没有评论,来添加一个吧(●'◡'●)