在数据中台作 BI 工具常常面对海量数据的渲染处理,除了组件自己性能优化以外,常常要排查总体页面性能瓶颈点,尤为是维护一些性能作得并很差的旧代码时。前端
React 性能调试是面对这种问题的必修课,借助Profiling React.js Performance这篇文章一块儿学习一下这个技能吧。react
本文介绍了众多性能检测工具与方法。git
Profiler
这个 API 是一种运行时 Debug 的补充,能够经过其 callback 拿到组件渲染信息,用法以下:github
const Movies = ({ movies, addToQueue }) => ( <React.Profiler id="Movies" onRender={callback}> <div /> </React.Profiler> ); function callback( id, phase, actualTime, baseTime, startTime, commitTime, interactions ) {}
这个 callback 会在每次渲染时执行,渲染分为初始化和更新阶段,经过phase
区分,下面是参数详细说明:json
setState
或 hooks changed 之类。注意尽可能不要轻易使用Profiler
检测性能,由于Profiler
自己也会消耗性能。浏览器
若是不想得到这么详细的渲染耗时,或者不想提早在代码中埋点,能够利用 DevTools 的 Profiler 查看更直观更简洁的渲染耗时:性能优化
其中 Ranked 能够展现按照渲染耗时排序后的结果,Interations 须要配合 Tracing API 使用,在后面会提到。服务器
利用scheduler/tracing
提供的trace
API,咱们能够记录某个动做的耗时,好比 “点击添加按钮收藏一个电影” 耗时多久:微信
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 }); }); }; }
在 Interations 中能够看到动做触发的耗时:网络
这个动做还能够是渲染,好比能够记录 ReactDOM 渲染的耗时:
import { unstable_trace as trace } from "scheduler/tracing"; trace("initial render", performance.now(), () => { ReactDom.render(<App />, document.getElementById("app")); });
甚至还能够追踪异步的耗时:
import { unstable_trace as trace, unstable_wrap as wrap, } from "scheduler/tracing"; trace("Some event", performance.now(), () => { setTimeout( wrap(() => { // 异步操做 }) ); });
有了Profiler
与trace
这两件武器,咱们能够监控任意元素的渲染耗时与交互耗时,几乎能够涵盖全部性能监控须要。
咱们还能够利用 Puppeteer 实现自动化操做并打印报告:
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(); })();
首先利用puppeteer
建立一个浏览器,新建一个页面并打开https://react-movies-queue.glitch.me/
这个 URL,等待页面加载完毕后利用 DOM 选择器找到按钮,利用page.click
API 模拟点击这个按钮,并在先后利用page.tracing
记录性能变化,并将这个文件上传到 DevTools Performance 面板,就会获得一份自动的性能检测报告:
这张图至关重要,是浏览器综合运行开销分析的利器,最上面分为 4 个部分:
下面会有一张 Network 详细图解,好比这张图:
细线表示等待的时间,粗线表示实际加载的状况,其中浅色部分表示服务器等待时间,即从发送下载请求到服务器响应第一个字节的时间。这部分能够看出资源并行加载阻塞状况以及资源服务器响应时间是否存在问题。
Timings 展现了几个重要时间节点,这里列举一部分:
再下面是 JS 计算消耗,用了一张火焰图,火焰图是性能分析的经常使用可视化工具。如下面这张图为例:
看火焰图首先看跨度最长的函数,也就是最长的那条线,这是最耗时的部分,从左到右是浏览器脚本的调用顺序,从上到下是函数嵌套的顺序。
咱们能够看到鼠标位置的 34 这个函数虽然长,但并非性能瓶颈,由于下面执行的 n 函数长度和它同样,表示 34 函数的性能几乎无损耗,其性能由其调用的 n 函数决定。
咱们能够利用这种方式一步步排查到叶子结点,找到对性能影响最大的元子函数。
咱们还能够利用performance.mark
自定义性能检测节点:
// 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");
这些节点能够在上面介绍的 Performance 面板中展现出来用于自定义分析。
利用 Performance 进行通用性能分析,利用 React Profiler 进行 React 定制性能分析,这两个结合在一块儿几乎能够完成任何性能检测。
通常来讲,首先应该用 React Profiler 进行 React 层面的问题筛查,这样更直观,更容易定位问题。若是某些问题跳出了 React 框架范围,或者再也不能以组件粒度进行度量,咱们能够回到 Performance 面板进行通用性能分析。
讨论地址是: 精读《React 性能调试》 · Issue #247 · dt-fe/weekly
若是你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权声明:自由转载-非商用-非衍生-保持署名( 创意共享 3.0 许可证)