React 已經比較快了,但使用時仍很容易犯些小錯誤,導致其效能下降。元件掛載慢,元件樹層級過深以及沒必要的迴圈渲染都會讓app感覺很慢。
幸虧有很多工具(有些甚至內建在 React 中)能幫助診斷效能問題。在這篇文章中,我將告訴你快速生成 React 應用程式的工具和技術。每個部分會有一個互動以及(希望是)有趣的演示!
工具1:The Performance Timeline
React 15.4.0 介紹了一種新的效能 timeline 特性可以讓你看到元件安裝、更新和解除安裝的時間,你還可以看到相關視覺化元件的生命週期。
注:現在,該功能只適用於Chrome,Edge 和 IE,因為它實用的 User Timing API 尚未在所有瀏覽器上實現。
如何執行它:
- 開啟你的 app 並附加查詢引數: react_perf。例如,http://localhost:3000?react_perf
- 開啟 Chrome DevTools 的 Performance,點選 Record.
- 執行要分析的操作。
- 停止記錄。
- 檢查 User Timing.
瞭解輸出
每個顏色欄顯示一個元件“工作”的時間。因為JavaScript是單執行緒的,當一個元件安裝或渲染,都會佔用主執行緒並防止其他程式碼執行。
括號內是 [update] 描述的是元件生命週期的哪一部分正在執行。timeline會把每個步驟分解,所以你可以區分出諸如[componentDidMount][componentWillReceiveProps][ctor](constructor) 和[render]的細微的時間差。
堆疊的條表示元件樹。雖然在 React 中有相當深的元件樹是典型的,但是如果您正在優化頻繁安裝的元件,它可以幫助減少包裝器元件的數量,因為每個元件都有增加一點效能和記憶體的損耗。
這裡需要注意的一點是,timeline中的數值是用於React開發構建的,所以相對於生產會慢許多。事實上,使用timeline也會使你的app變慢。雖然這些數值並不代表真實環境的效能,但是不同元件的相對時間是精確的。而且,元件是否更新並不取決於生產環境。
這裡需要注意的一點是,timeline中的數值是用於React開發構建的,所以用於實際生產時會感覺比較慢。事實上,使用 timeline 也會使你的 app 變慢。雖然這些數值並不代表真實環境的效能,但是不同元件的相對時間是精確的。另外,元件是否更新並不取決於生產環境。
Demo #1
為了好玩,我在 todomvc APP 增加了一些嚴重的效能問題。你可以在這裡試試。
要檢視 timeline,開啟 Chrome 開發工具,轉到“Performance”選項,然後單擊“Record”。再在App裡新增一些 TODOs ,停止記錄並檢視timeline。你可以看看到底是哪些元件引起了效能問題 :)
工具#2: why-did-you-update
影響 React 中效能的最常見問題之一是不必要的渲染迴圈。預設情況下,React 元件將在父類渲染時重新渲染,即使他們的 props 沒有改變。
例如,如果我有一個這樣的簡單元件:
1 2 3 4 |
class DumbComponent extends Component { render() { return <div> {this.props.value}</div> ; } } |
有一個這樣的父類元件:
1 2 3 4 |
class Parent extends Component { render() { return <div> </div>; } } |
無論何時父元件被渲染時,DumbComponent將被重新渲染,儘管其props並沒有改變。
通常,如果渲染執行完成,並且虛擬DOM並沒有改動,那麼它是一個無用的渲染迴圈,因為渲染方法應該是純的,而且沒有任何副作用的。在一個大規模的React應用程式中,檢測發生這種情況的發生的位置可能很棘手,但幸運的是,我們有一個可以幫助檢測的工具!
使用 why-did-you-update
why-did-you-update 是一個掛鉤到 React 檢測潛在的不必要的元件渲染的庫。即使props 沒有改變,當一個元件的渲染方法被呼叫時它也會進行檢測。
安裝程式:
- npm 安裝:npm i –save-dev why-did-you-update
- 在應用程式的任何地方新增此程式碼:
1 2 3 4 5 |
import React from 'react' if (process.env.NODE_ENV !== 'production') { const {whyDidYouUpdate} = require('why-did-you-update') whyDidYouUpdate(React) } |
注:該工具用於本地開發很好,但要確保生產環境下被禁用,因為它會讓你的應用變慢。
理解輸出結果
what-did-you-update用於監測你的應用程式,記錄其執行情況和可能出現的不必要地元件變動。它可以讓你在渲染迴圈之前和之後看到其屬性以確定該過程是否是不必要的。
Demo #2
為了說明why-did-you-update的原理, 我將該庫安裝在Code Sandbox中的TodoMVC應用程式上,這是一個線上React遊戲機。開啟瀏覽器控制檯並新增一些TODOs,檢視輸出。
演示地址.
請注意,應用程式中有幾個元件是沒有必要渲染的。嘗試使用上述技術來預防不必要的渲染。如果正確地完成,那麼在控制檯中應該沒有任何why-did-you-update的輸出。
工具 3: React Developer Tools
React Developer Tools Chrome Chrome擴充套件有一個內建的視覺化元件更新功能。這有助於檢測不必要的 render 週期。要使用它,首先要在這裡安裝擴充套件。
然後,開啟擴充套件通過點選“React”選項卡中DevTools並檢查“高亮更新(Highlight Updates)”。
然後,簡單地使用你的應用程式。使元件互動,看 DevTools發揮它的魔力。
瞭解輸出
React Developer Tools 高亮顯示在給定時間內重複渲染的元件。使用藍色、綠色、黃色和紅色表示更新的頻率。
看到黃色或紅色的不一定是壞事。當調整 slider 或 UI 元素頻繁觸發更新時就會出現這種情況。但是如果你點選一個簡單的按鈕,看到紅色 —— 那可能就是什麼東西出了問題。該工具的目的是發現更新不必要的元件。作為應用程式開發人員,您應該有一個大致的概念,在給定的時間內哪些元件應該更新。
demo #3
為了演示元件高亮,我在 TodoMVC 應用裡更新了一些不必要的元件。
開啟上面的連結,然後開啟 React Developer Tools 並啟用更新高亮。當您鍵入的文字輸入頭部時就會看到所有TODOs不必要的高亮。當你輸入加快的時候,你會看到顏色的變化得更頻繁。
修復不必要的渲染問題
一旦定位到了應用中的不必要重繪的元件,可以用上這些簡單的修復方法。
使用 purecomponent
在上面的例子中, dumbcomponent 是其 props 的純函式。也就是說,元件只需要在 props改變時重新渲染。React 有一個特殊型別的內建元件叫 PureComponent ,說的就是這種使用案例。
從React.PureComponent繼承而不是從React.Component.
1 2 3 4 |
class DumbComponent extends PureComponent { render() { return <div> {this.props.value}</div>; } } |
然後,元件只在props發生變化時才重新渲染。就是這麼回事!
注意, purecomponent 只對 props 做簡單對比,所以如果你使用的是複雜的資料結構,可能會導致錯過一些 prop 的變化而使你的元件得不到更新。
實現 shouldComponentUpdate
shouldComponentUpdate 是一個元件方法,當 props 或 state 改變時,執行render之前就會被呼叫。如果 shouldcomponentupdate 返回true,render 將被呼叫,如果它返回false,則什麼也不會發生。
這種方法可以通過判斷它的 props 有沒有改變讓 React 避免重新渲染給定的元件。
例如,在上述例子中的 dumb component 實現shouldComponentUpdate
1 2 3 4 5 6 7 8 9 10 11 |
class DumbComponent extends Component { shouldComponentUpdate(nextProps) { if (this.props.value !== nextProps.value) { return true; } else { return false; } } render() { return <div>foo</div>; } } |
除錯中的效能問題
如果您在自己的機器上執行應用程式,則React開發工具僅僅只是工作。如果你想在瞭解使用者感興趣的效能問題,試一下 LogRocket。
LogRocket 就像DVR 之於 Web應用程式,書面記錄一切發生在你的網站上的情況。建議不要猜測問題發生的原因,而是通過重現 bug 或效能問題以快速瞭解問題的根本原因。
LogRocket 儀器記錄 app 的效能資料,Redux 行為/狀態、日誌、錯誤、網路請求/響應資訊頭+資訊主體,和瀏覽器後設資料。它還記錄頁面上的HTML和CSS,重新生成甚至是最複雜的單頁應用程式的超高清視訊。