React.js 性能分析
摘要:今天我將向大家演示如何使用 React Profiler API 、 Tracing API 以及 User Timing API 來分別追蹤 React 的組件渲染、用戶交互以及自定義性能指標。爲了捕獲 DevTools 對當前運行程序性能的追蹤,Puppeteer 提供了 trace .start() 和 trace.stop() 兩個 API,下面我們就用它來追蹤按鈕點擊的過程,代碼如下:。
<div>
今天我將向大家演示如何使用 React Profiler API 、 Tracing 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 在它的開發包中用 Profiler 替代了 User Timings ,不過由於 User Timings 的時間精度更高,所以可能會在未來的 3 級規格的瀏覽器中重新添加它。
- Next.js-hydration: 混合持續時間
- Next.js-nav-to-render: 導航開始到開始渲染之間的時間