在react開發中,常常會遇到組件重複渲染的問題,父組件一個state的變化,就會致使以該組件的全部子組件都重寫render,儘管絕大多數子組件的props沒有變化vue
首先,先上一張react生命週期圖:react
這張圖將react的生命週期分爲了三個階段:生成期、存在期、銷燬期,這樣在create、props、state、unMount狀態變化時咱們能夠清楚的看到reacte觸發了哪些生命週期鉤子以及何時會render。git
若是咱們須要更改root的一個state,使綠色組件視圖更改github
若是你寫過vue,你會發現組件更新是如上圖那樣的(視圖指令已編譯爲修改視圖的函數存放在綁定的state裏的屬性裏,因此可以作到靶向修改),而react會以組件爲根,從新渲染整個組件子樹,以下圖(綠色是指望的render路徑,橙色是無用render):數組
因此在react裏,咱們探討的render性能優化是react調用render的路徑以下:性能優化
shouldComponentUpdate(nextProps, nextState)
使用shouldComponentUpdate()以讓React知道當前狀態或屬性的改變是否不影響組件的輸出,默認返回ture,返回false時不會重寫render,並且該方法並不會在初始化渲染或當使用forceUpdate()時被調用,咱們要作的只是這樣:數據結構
shouldComponentUpdate(nextProps, nextState) { return nextState.someData !== this.state.someData }
可是,state裏的數據這麼多,還有對象,還有複雜類型數據,react的理念就是拆分拆分再拆分,這麼多子組件,我要每一個組件都去本身一個一個對比嗎??不存在的,這麼麻煩,要知道咱們的終極目標是坐享其成-_-app
React.PureComponent 與 React.Component 幾乎徹底相同,但 React.PureComponent 經過props和state的淺對比來實現 shouldComponentUpate()。若是對象包含複雜的數據結構,它可能會因深層的數據不一致而產生錯誤的否認判斷(表現爲對象深層的數據已改變視圖卻沒有更新)函數
關注點:
const hasOwnProperty = Object.prototype.hasOwnProperty; /** * inlined Object.is polyfill to avoid requiring consumers ship their own * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is */ function is(x: mixed, y: mixed): boolean { // SameValue algorithm if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 // Added the nonzero y check to make Flow happy, but it is redundant return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } } /** * Performs equality by iterating through keys on an object and returning false * when any key has values which are not strictly equal between the arguments. * Returns true when the values of all keys are strictly equal. */ function shallowEqual(objA: mixed, objB: mixed): boolean { if (is(objA, objB)) { return true; } if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. for (let i = 0; i < keysA.length; i++) { if ( !hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } } return true; }
針對以上規則咱們在項目開發種能夠作出以下優化:性能
儘可能將複雜類型數據(ArrayList)所關聯的視圖單獨拆成PureComonent有助於提升渲染性能,好比表單、文本域和複雜列表在同一個 render() 中,表單域的輸入字段改變會頻繁地觸發 setState() 從而致使 組件 從新 render()。而用於渲染複雜列表的數據其實並無變化,但因爲從新觸發 render(),列表仍是會從新渲染。
我想複雜數組沒變化時也不要render(), 那你用react-immutable-render-mixin,來,咱們看看插件的介紹:
Users are urged to use PureRenderMixin with facebook/immutable-js. If performance is still an issue an examination of your usage of Immutable.js should be your first path towards a solution. This library was created from experimentations with Immutable that were ultimately erroneous; improper usage of Immutable.js 💩. Users should be able to achieve maximum performance simply using PureRenderMixin.
譯:不能以正確的姿式來使用immutable-js作優化,你就不要瞎折騰了,用它react-immutable-render-mixin就好了
它和ProComponent原理同樣,惟一的區別就是新舊數據的對比,react-immutable-render-mixin用了immutable-js 的is()方法去作對比,性能強,複雜類型數據也能對比(這裏不對immutable-js作討論,一篇很不錯的文章Immutable 詳解及 React 中實踐),相比於React.PureComponent更方便---源碼
import Immutable from 'immutable'; const is = Immutable.is.bind(Immutable); export default function shallowEqualImmutable(objA, objB) { if (objA === objB || is(objA, objB)) { return true; } if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. const bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB); for (let i = 0; i < keysA.length; i++) { if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { return false; } } return true; }
用法不少,我喜歡Decorator:
import React from 'react'; import { immutableRenderDecorator } from 'react-immutable-render-mixin'; @immutableRenderDecorator class Test extends React.Component { render() { return <div></div>; } }