写点什么

React.js 性能分析

  • 2020-04-27
  • 本文字数:3300 字

    阅读完需:约 11 分钟

React.js性能分析

今天我将向大家演示如何使用 React Profiler APITracing API 以及 User Timing API 来分别追踪 React 的组件渲染、用户交互以及自定义性能指标。


本文最初发布于Addy Osmani博客,经原作者授权由 InfoQ 中文站翻译并分享。


下面我会用一个影片排期的应用做具体的演示(译者注:应用效果图如下)。


React Profiler API

首先来了解下React Profiler,它主要用来追踪应用组件的渲染过程以及渲染开销,同时标记出应用的性能瓶颈。Profiler 接受一个 onRender 回调函数,当被追踪的组件以及子代组件发生更新时,该函数就会被调用。下图是在影片排期应用中使用 Profiler 追踪各个组件渲染:



Profiler 中 onRender 回调函数的具体参数如下:


  • id:这是 Profiler 的唯一标示,区分是哪个 Profiler 追踪的组件树发生了更新

  • phase:如果更新是挂载阶段这个值就是“mount”,如果是二次渲染阶段就是“update”

  • actualDuration:更新花费的渲染时间

  • baseDuration:更新预计花费的渲染时间

  • startTime:更新开始时间点

  • commitTime:更新提交的时间点

  • 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 调试器中可以看到如下输出:



也可以打开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>)
复制代码


知道了如何追踪组件渲染,那么如果想跟踪交互,该怎么做

交互追踪 Tracing API

想一下,如果能追踪到交互(例如:按钮的点击),那么在回答“这个按钮点击花费了多少时间更新 DOM?”这样的问题时是不是就有了依据。要感谢 Brian Vaughn 的努力,React 在其调度包中引入了对这个功能的试验支持,更详细的说明可以点击这里查看。


一个交互追踪,需要包含一个描述(例如:添加购物车按钮被点击)、一个时间戳和一个回调函数,在回调函数中你可以定义一些和该交互相关的逻辑。在“影片排期应用”中就有一个添加电影到播放列表的“+”号按钮,这个就是一个交互按钮。



下面的代码演示了如何追踪这个按钮的点击行为:


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 开发调试工具的 interaction 面板中可以看到具体的交互行为和持续时间:



这个 API 同样也可以追踪初始化渲染


import { unstable_trace as trace } from "scheduler/tracing";trace("initial render", performance.now(), () => {   ReactDom.render(<App />, document.getElementById("app"));})
复制代码



Brian 提供了更多的例子,比如如何追踪异步行为等。这些示例都在其“React中进行交互追踪”项目的 gist 中。

Puppeteer 的使用

如果想对 UI 交互追踪脚本做进一步了解的话,你可能会对 Puppeteer 这个库感兴趣。Puppeteer 是一个 Node 库,基于 Chrome 开发协议封装 API 来操作 headless Chrome(译者注:Chrome 浏览器对无界面形态)。


为了捕获 DevTools 对当前运行程序性能的追踪,Puppeteer 提供了trace .start()和trace.stop()两个 API,下面我们就用它来追踪按钮点击的过程,代码如下:


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);  // 开始追踪...  await page.tracing.start({ path: 'profile.json' });  // 按钮点击  await page.click(addMovieToQueueBtn);  // 停止追踪  await page.tracing.stop();  await browser.close();
复制代码


然后在开发工具的性能面板中导入 profile.json,我们就可以看到当按钮点击的时候,所有函数的调用情况:



如果你对交互追踪感兴趣并且想了解更多的话,不妨看看 Stoyan Stefanov 的“JavaScript组件级别的CPU开销”这篇文章。

客户端性能追踪 API

使用客户端性能追踪API可以追踪一些定制的性能指标,并且时间精确度会更高。它有 2 个主要的 API:


  • window.performance.mark():存储当前 mark 执行时的时间戳

  • window.performance.measure():存储 2 个相同 mark 之间的执行时间


示例代码如下:


// 记录任务开始之前的时间戳performance.mark('Movies:updateStart');// 这里执行了一些任务...// 记录任务结束的时间戳performance.mark('Movies:updateEnd');// 计算任务开始前后的差值performance.measure('moviesRender', 'Movies:updateStart', 'Movies:updateEnd');
复制代码


当你通过 Chrome 调试工具中的性能面板查看一个 React 应用时,有一个“Timings”的区域,这里归集了你的 React 组件的执行时间。在渲染时,React 会把通过客户端 API 得到的性能数据发布到这里。




在互联网上,你会发现有一些其他的 React 应用已经在使用 User Timing 追踪他们的自定义指标,包括 Reddit 网站中的“到第一标题可见花费的时间”和 Spotify 网站中的“到回放准备完毕花费的时间”。



还可以在 Chrome 调试器的Lighthouse面板中查看到定制化的User Timing标记和追踪方法,如下图:



Next.js的最近版本中也针对一些事件添加了很多 User timing 标记和追踪,例如:


  • Next.js-hydration:混合持续时间

  • Next.js-nav-to-render:导航开始到开始渲染之间的时间


所有的这些追踪都可以在调试器的 Timings 区域看到:


对比 DevTools 和 Lighthouse

值得注意的是,LighthouseChrome调试工具中的性能面板都可以深入分析 React 应用程序的加载和运行时性能,用户可以看到下面这些性能指标:



React 用户可能会喜欢像总阻塞时间(TBT)这样的新指标,它量化一个页面具体什么时候才可以交互(可交互时间), 下面我们可以看下在并发模式前后应用发生更新时,TBT 的情况:



这些工具一般能帮助我们了解在浏览器级别的视图性能瓶颈,例如,哪些繁重冗长的任务会引起交互延迟(例如按钮点击响应) :



Lighthouse 还为一些特定的性能场景提供了修改建议。如在Lighthouse 6.0中可以看到一个提示,建议我们移除未使用的JavaScript代码。Lighthouse 追踪到了这个问题并且提醒我们可以使用 React.lazy ()来引入这个 JavaScript。



借助用户端的硬件进行性能智能检查,往往对性能分析非常有帮助。


最后,除了上面提到的我通常还会从RUM 和 CrUX获取一些数据字段,然后用webpagetest.org/easy工具帮我生成更多的场景图片,以便更好的进行性能分析。

更多文章


如果你对文章中的 demo 有兴趣,可以点击查看在线demo,或者从Glitch上下载源码。


原文链接:Profiling React.js Performance


2020-04-27 12:123040

评论 1 条评论

发布
暂无评论
发现更多内容

关于计算机面试重难点 之 操作系统,字节架构师有话说

Java 程序员 后端

凭借着这份Spring面试题,我拿到了阿里,字节跳动美团的offer!

Java 程序员 后端

北上广深,2020,多少K的Java程序员应该懂高并发多线程和JVM优化

Java 程序员 后端

华山论剑!滴滴CTO五轮面试真是太刺激了,已拿到offer

Java 程序员 后端

初识Servlet

Java 程序员 后端

别再说你不会-JVM-性能监控和调优了,看完这篇再发言!

Java 程序员 后端

制作Docker镜像,用来编译OpenJDK11源码

Java 程序员 后端

力扣前400题解答笔记,全被字节大神整理到了这份文档里

Java 程序员 后端

华为java工程师的提升程序员实力的几点建议

Java 程序员 后端

几款常见接口管理平台对比

Java 程序员 后端

分布式服务下,消息中间件改造

Java 程序员 后端

分布式架构——Gossip 协议详解

Java 程序员 后端

初次远程面试蚂蚁金服,三面过后本以为凉凉,没想到直接被录取了

Java 程序员 后端

别再找了,这就是全网最全的SpringBean的作用域管理!

Java 程序员 后端

前华为18A架构师,总结“RabbitMQ”开发手册,已开源

Java 程序员 后端

剖根问底:Java 不能实现真正泛型的原因是什么?

Java 程序员 后端

动手造轮子:实现一个简单的-AOP-框架

Java 程序员 后端

区块链编程七大语言,使用最多的竟是Java

Java 程序员 后端

南邮《网络技术与应用》4次作业

Java 程序员 后端

初识Java语言(六)- 多态、抽象类以及接口

Java 程序员 后端

前后端项目练习(整合Spring)

Java 程序员 后端

刚拿的字节跳动offer“打水漂”,TikTok不去了,我该何去何从

Java 程序员 后端

初探DispatcherServlet#doDispatch

Java 程序员 后端

十面阿里Java岗,看我怎么吊打面试官!

Java 程序员 后端

华为安全技术专家与Linux内核到底发生了什么?这本小册子是怎么回事?

Java 程序员 后端

互联网通信云盛会WICC广州站绿色报名通道开启

融云 RongCloud

十个超酷的java谋生方式,你喜欢吗?

Java 程序员 后端

华为初面+综合面试(Java技术面)附上面试题

Java 程序员 后端

内卷严重!看看这些java核心资料,提高竞争力,争做拍死别人的后浪

Java 程序员 后端

冷门的 Java 应用程序安全沙箱机制了解一下

Java 程序员 后端

创建型模式之建造者模式——链式调用

Java 程序员 后端

React.js性能分析_语言 & 开发_Addy Osmani_InfoQ精选文章