在react專案中使用shouldComponentUpdate方法進行元件效能優化
在react專案中使用shouldComponentUpdate方法進行元件效能優化,前半部分主要是思路和官方方法解釋,最後兩張大圖為測試對比結果。
react的渲染流程主要是
初始化元件
該階段會執行元件及其所有子元件的render方法,從而生成第一版的虛擬dom。
元件更新渲染
元件的props或者state任意發生改變就會觸發元件的更新渲染。預設情況下其也會執行該元件及其所有子元件的render方法獲取新的虛擬dom。
接下來我們聊聊元件更新渲染的效能問題。
react元件更新流程
通過上面分析可以知道元件更新具體過程如下:
執行該元件及其所有子元件的render方法獲取更新後的虛擬DOM,即re-render,即使子元件無需更新。
然後對新舊兩份虛擬DOM進行diff來進行元件的更新
在這個過程中,可以通過元件的shouldComponentUpdate方法返回值來決定是否需要re-render。
react的整個更新渲染流程可以借用一張圖來加以說明:
預設地,元件的shouldComponentUpdate返回true,即React預設會呼叫所有元件的render方法來生成新的虛擬DOM, 然後跟舊的虛擬DOM比較來決定元件最終是否需要更新。
效能瓶頸分析
借圖說話,例如下圖是一個元件結構tree,當我們要更新某個子元件的時候,如下圖的綠色元件(從根元件傳遞下來應用在綠色元件上的資料發生改變):
理想情況下,我們只希望關鍵路徑上的元件進行更新,如下圖:
但是,實際效果卻是每個元件都完成re-render和virtual-DOM diff過程,雖然元件沒有變更,這明顯是一種浪費。如下圖黃色部分表示浪費的re-render和virtual-DOM diff。
根據上面的分析,react的效能瓶頸主要表現在:
對於props和state沒有變化的元件,react也要重新生成虛擬DOM及虛擬DOM的diff。
這個時候,就是shouldComponentUpdate上場的時候了。
shouldComponentUpdate(nextProps, nextState){ return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state) }其中,isEqual方法為判斷兩個物件是否相等(指的是其物件內容相等,而不是全等)。
通過顯示覆蓋shouldComponentUpdate方法來判斷元件是否需要更新從而避免無用的更新,但是若為每個元件新增該方法會顯得繁瑣,好在react提供了官方的解決方案,具體做法:
方案對元件的shouldComponentUpdate進行了封裝處理,實現對元件的當前屬性和狀態與上一次的進行淺對比,從而決定元件是否需要更新。
react在發展的不同階段提供兩套官方方案:
PureRenderMin
一種是基於ES5的React.createClass建立的元件,配合該形式下的mixins方式來組合PureRenderMixin提供的shouldComponentUpdate方法。當然用ES6建立的元件也能使用該方案。
import PureRenderMixin from 'react-addons-pure-render-mixin'; class Example extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); }PureComponent
該方案是在React 15.3.0版本釋出的針對ES6而增加的一個元件基類:React.PureComponent。這明顯對ES6方式建立的元件更加友好。
import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }需要指出的是,不管是PureRenderMin還是PureComponent,他們內部的shouldComponentUpdate方法都是淺比較(shallowCompare)props和state物件的,即只比較物件的第一層的屬性及其值是不是相同。例如下面state物件變更為如下值:
state = { value: { foo: 'bar' } }因為state的value被賦予另一個物件,使nextState.value與this.props.value始終不等,導致淺比較通過不了。在實際專案中,這種巢狀的物件結果是很常見的,如果使用PureRenderMin或者PureComponent方式時起不到應有的效果。
雖然可以通過深比較方式來判斷,但是深比較類似於深拷貝,遞迴操作,效能開銷比較大。
為此,可以對元件儘可能的拆分,使元件的props和state物件資料達到扁平化,結合著使用PureRenderMin或者PureComponent來判斷元件是否更新,可以更好地提升react的效能,不需要開發人員過多關心。
案例分析
TimeLineView 列表頁元件面
TimeLineEventView 列表item元件頁面
說明:列表頁面 資料list.map(TimeLineEventView)
現有一個列表頁面,滾動載入下一頁,每個專案中均有一個點贊功能,按照邏輯我們一般是在列表jsx頁面通過資料list.map(頁面item方法)來實現,通過頁面state來處理點贊按鈕的樣式切換:
我們不對列表item進行shouldComponentUpdate處理,下面通過Perf工具來檢測當我們點選某一個item贊功能的時候,通過Perf輸出頁面效能檢測結果:
如上圖的Print Wasted 模組,我們可以看到當前列表頁面,我們點選某一個item的贊功能,頁面會產生將近20ms的效能損耗,主要就是item元件 TimeLineEventView 被重新渲染了17次,而這17次都是沒有任何變化的item產生的。
然後我們改造TimeLineEventView頁面,增加shouldComponentUpdate處理:
shouldComponentUpdate(nextProps, nextState) { if (this.props.IsPraised == nextProps.IsPraised) { return false; } return true; }上面的程式碼就是充寫了react預設返回true的shouldComponentUpdate方法,通過邏輯可知,只有被點選的item元件才會返回true,其他均為false,這樣就會實現非相關元件不會重新render渲染,改造完畢後,我們再次通過Perf工具進行檢測測試,結果如下:
繼續看上圖的Print Wasted 模組,可以看到,已經沒有 TimeLineEventView 相關的元件渲染損耗記錄了,說明我們優化成功,目前的渲染損耗在1ms左右,雖然從 20ms 到 1ms並不會對使用者產生很大的感知,但是對於滾動載入列表來說,如果資料結構複雜,頁面資料量達到幾百條,那麼損耗的效能可能就是7.8百ms,對於頁面來說就會很明顯的卡頓等,所以,對元件進行足夠、合理的扁平化拆分就顯得尤為必要了。
參考:
http://www.cnblogs.com/wonyun/p/6804952.html
http://benchling.engineering/deep-dive-react-perf-debugging/
https://segmentfault.com/a/1190000006741060
React Perf chrome外掛地址:
https://chrome.google.com/webstore/detail/react-perf/hacmcodfllhbnekmghgdlplbdnahmhmm
內容均為作者獨立觀點,不代表八零IT人立場,如涉及侵權,請及時告知。
相關文章
- 使用shouldComponentUpdate進行效能優化優化
- React 渲染優化:diff 與 shouldComponentUpdateReact優化
- 在React專案中,如何優雅的優化長列表React優化
- React Native 效能優化元件-PureComponentReact Native優化元件
- 4、React元件之效能優化React元件優化
- 優雅的在React專案中使用ReduxReactRedux
- React函式式元件的效能優化React函式元件優化
- [譯]使用React.memo()來優化函式元件的效能React優化函式元件
- 使用React中後臺效能優化以及移動端優化React優化
- 在 React 專案中全量使用 HooksReactHook
- react效能優化React優化
- React 效能優化React優化
- 在Linux中,如何進行系統效能調優?Linux
- TypeScript在React專案中的使用總結TypeScriptReact
- React效能優化:PureComponent的使用原則React優化
- react/react-native效能優化React優化
- react-效能優化React優化
- React渲染效能優化React優化
- 藉助 webpack 對專案進行分析優化Web優化
- [譯] 拖放庫中 React 效能的優化React優化
- React元件中對子元件children進行加強React元件
- 優雅的在React元件中註冊事件React元件事件
- vuejs專案效能優化總結VueJS優化
- 在TypeScript專案中進行BDD測試TypeScript
- 在vue專案中優雅的使用SvgVueSVG
- React中型專案的優化實踐React優化
- vue專案可以從哪些方面進行優化Vue優化
- Cookbook:優化 Vue 元件的執行時效能優化Vue元件
- React+Webpack效能優化ReactWeb優化
- React 效能優化總結React優化
- React效能優化總結React優化
- mysql 大表中count() 使用方法以及效能優化.MySql優化
- Vue 專案效能優化 — 實踐指南Vue優化
- 在React專案中安裝並使用Less(用法總結)React
- 在 Create React App 專案中使用 PrettierReactAPP
- React效能優化方案之PureRenderMixinReact優化
- React效能優化方案之PureComponentReact優化
- TypeScript在react專案中的實踐TypeScriptReact