摘要:今天我將向大家演示如何使用 React Profiler API 、 Tracing API 以及 User Timing API 來分別追蹤 React 的組件渲染、用戶交互以及自定義性能指標。爲了捕獲 DevTools 對當前運行程序性能的追蹤,Puppeteer 提供了 trace .start() 和 trace.stop() 兩個 API,下面我們就用它來追蹤按鈕點擊的過程,代碼如下:。

        <div> 
今天我將向大家演示如何使用 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: 更新中包含的交互信息
複製代碼
constcallback =(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>
<Profilerid="Header"onRender={callback}>
<Header{...props}/>
</Profiler>
<Profilerid="Movies"onRender={callback}>
<Movies{...props}/>
</Profiler>
</App>
)

知道了如何追蹤組件渲染, 那麼如果想跟蹤交互,該怎麼做

交互追蹤 Tracing API

想一下,如果能追蹤到交互(例如:按鈕的點擊),那麼在回答“這個按鈕點擊花費了多少時間更新 DOM?”這樣的問題時是不是就有了依據。要感謝 Brian Vaughn 的努力,React 在其 調度包 中引入了對這個功能的試驗支持,更詳細的說明可以點擊 這裏 查看。 一個交互追蹤,需要包含一個描述(例如:添加購物車按鈕被點擊)、一個時間戳和一個回調函數,在回調函數中你可以定義一些和該交互相關的邏輯。在“影片排期應用”中就有一個添加電影到播放列表的“+”號按鈕,這個就是一個交互按鈕。 下面的代碼演示瞭如何追蹤這個按鈕的點擊行爲: 複製代碼
import{ unstable_Profiler asProfiler} from"react";
import{ render } from"react-dom";
import{ unstable_trace as trace } from"scheduler/tracing";
classMyComponentextendsComponent{
addMovieButtonClick = event => {
trace("Add To Movies Queue click", performance.now(), () => {
this.setState({ itemAddedToQueue:true});
});
};
在 React 開發調試工具的 interaction 面板中可以看到具體的交互行爲和持續時間: 這個 API 同樣也可以 追蹤初始化渲染 複製代碼
import{ unstable_traceastrace }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,下面我們就用它來追蹤按鈕點擊的過程,代碼如下: 複製代碼
constpuppeteer =require('puppeteer');
(async() => {
constbrowser =awaitpuppeteer.launch();
constpage =awaitbrowser.newPage();
constnavigationPromise = page.waitForNavigation();
awaitpage.goto('https://react-movies-queue.glitch.me/')
awaitpage.setViewport({ width: 1276, height: 689 });
awaitnavigationPromise;
constaddMovieToQueueBtn = 'li:nth-child(3) > .card > .card__info > div > .button';
awaitpage.waitForSelector(addMovieToQueueBtn);
// 開始追蹤...
awaitpage.tracing.start({ path: 'profile.json' });
// 按鈕點擊
awaitpage.click(addMovieToQueueBtn);
// 停止追蹤
awaitpage.tracing.stop();
awaitbrowser.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 工具幫我生成更多的場景圖片,以便更好的進行性能分析。
相關文章