分析 React 组件的渲染性能
source link: http://mp.weixin.qq.com/s?__biz=Mzg2NDAzMjE5NQ%3D%3D&%3Bmid=2247485881&%3Bidx=1&%3Bsn=68b410e32c488556700b72aada9843df
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
今天,我们介绍一下如何使用 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
特殊定制的审计:
译自 https://addyosmani.com/blog/profiling-react-js/
轻点在看,支持作者:heart:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK