1 shouldeComponentUpdate

shouldComponentUpdate應該是提到的優化方法裡面提到最多的,使用的也是最多的,如果返回false,就會阻止下面組件的渲染,反之,返回true,就會進行接下來的渲染,默認返回的事true。但是有個問題,shouldComponentUpdate進行的是淺比較,為什麼不進行深比較,因為傷不起,深比較的耗費性能可能比每次render的效率還要低。

class Nest extends React.Component {
shouldComponentUpdate = (nextProps, nextState) => {
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState)
)
}
render() {
console.log(inner)
return <div>Nest</div>
}
}

為什麼進行的事淺比較,我們從源碼方面來看一下:

const hasOwn = Object.prototype.hasOwnProperty

function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}

export default function shallowEqual(objA, objB) {
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

for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}

return true
}

首先是Object.is的Polyfill,為什麼要用這個來比較而不是 == 或者 === 呢?

使用 == 會 出現以下bug:

0 == // true
null == undefined // true
[1] == true // true

使用 === 會出現以下bug:

+0 === -0 // true,
NaN === NaN // false,

Object.is修復了=== 這兩種判斷不符合預期的情況,並null,undefined,number,string,boolean做出非常精確的比較,以下幾種情況都會返回true。

  • 兩個值都是 undefined
  • 兩個值都是 null
  • 兩個值都是 true 或者都是 false
  • 兩個值是由相同個數的字元按照相同的順序組成的字元串
  • 兩個值指向同一個對象
  • 兩個值都是數字並且
    • 都是正零 +0
    • 都是負零 -0
    • 都是 NaN
    • 都是除零和 NaN 外的其它同一個數字

當對比的類型為Object的時候並且key的長度相等的時候,淺比較也僅僅是用Object.is()對Object的value做了一個基本數據類型的比較,所以如果key裡面是對象的話,就不能進行比較。

const hasOwn = Object.prototype.hasOwnProperty
// 下面就是進行淺比較了, 有不了解的可以提issue, 可以寫一篇對比的文章。
function is(x, y) {
// === 嚴格判斷適用於對象和原始類型。但是有個例外,就是NaN。
if (x === y) {
//這個是個例外,為了針對0的不同,譬如 -0 === 0 => true
// (1 / x) === (1 / y)這個就比較有意思,可以區分正負0, 1 / 0 => Infinity, 1 / -0 => -Infinity
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
// 這個就是針對上面的NaN的情況
return x !== x && y !== y
}
}

export default function shallowEqual(objA, objB) {
if (is(objA, objB)) return true //這個就是實行了Object.is的功能。實行的是SameValue策略。
// is方法之後,我們認為他不相等。不相等的情況就是排除了(+-0, NaN)的情況以及可以證明:
// 原始類型而言: 兩個不是同類型或者兩個同類型,值不同。
// 對象類型而言: 兩個對象的引用不同。

//下面這個就是,如果objA和objB其中有個不是對象或者有一個是null, 那就認為不相等。
//不是對象,或者是null.我們可以根據上面的排除來猜想是哪些情況:
//有個不是對象類型或者有個是null,那麼我們就直接返回,認為他不同。其主要目的是為了確保兩個都是對象,並且不是null。
if (typeof objA !== object || objA === null ||
typeof objB !== object || objB === null) {
return false
}

//如果上面沒有返回,那麼接下來的objA和objB都是對象了。

const keysA = Object.keys(objA)
const keysB = Object.keys(objB)

//兩個對象不同,有可能是引用不同,但是裡面的內容卻是相同的。例如:{a: a} ==~ {a: a}
//所以先簡單粗暴的判斷一級的keys是不是相同的長度。,不是那就肯定不相等,就返回false。
if (keysA.length !== keysB.length) return false

//下面就是判斷相同長度的key了
// 可以發現,遍歷的是objA的keysA。
//首先判斷objB是否包含objA的key,沒有就返回false。注意這個是採用的hasOwnPrperty來判斷,可以應付大部分的情況。
//如果objA的key也在ObjB的key里,那就繼續判斷key對應的value,採用is來對比。哦,可以發現,只會對比到第以及。

for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}

return true
}


2 React.PureComponent

個人覺得此優化手段適用於數據變化不太頻繁,如果只有一個或者展示類的組件可以使用,因為在父組件進行重新render時候,可以有效避免利用PureComponent的淺比較避免組件的渲染,不可以有每次都會變動的值,因為這樣你的 PureComponent 和 Component 其實沒兩樣。但是一下幾個小tip,可能避免組件重新render,其實這樣的點有很多,只是把我遇到的全部列舉出來。

  • props為空數組

<CustomList data={data || []} />

CustomList 在 data 值為 null 或 undefined 時,仍不會發生奔潰,但如果你這麼做,你會發現即使每次傳入的 props 都是[],仍然會發生 render ,原因在於[] !== [],他們的 reference 並不相同。解決辦法: 設置defaultProps 默認值。

  • props 為 inline function

<CustomList clickHandler={ () => this.setState({number: this.state.number + 1}) } />

這樣 每次點擊時,都會傳入一個新的function,又因為reference不同,導致每次都會渲染。

解決辦法: 將內連的function,進行預定義,拆出來定義。
  • props 使用的function類型 例如:

1 <CustomList clickHandler={this.handleClick } /> // constructor裡面綁定this;
2 <CustomList clickHandler={this.handleClick.bind(this) } />
3 <CustomList clickHandler={()=>this.handleClick.bind() } />

方式一: 構造函數每一次渲染的時候只會執行一遍; 性能最好;

方式二: 在每次render()的時候都會重新執行一遍函數;

方式三: 每一次render()的時候,都會生成一個新的箭頭函數,即使兩個箭頭函數的內容是一樣的,因為react判斷是否需要進行render是淺層比較,簡單來說就是通過===來判斷的,如果state或者prop的類型是字元串或者數字,只要值相同,那麼淺層比較就會認為其相同

當你使用 PureComponent 時,如果 props 或 state 要變動,可以嘗試使用 Immutable.js 來處理,避免有改了卻沒重新 render 的情況發生。


3. immutable.js

Immutable提供一直簡單快捷的方式以判斷對象是否變更,對於React組件更新和重新渲染性能可以有較大幫助。immutable可以幾乎在全家桶中結合,打算專門寫一篇介紹。

import { is } from immutable;

shouldComponentUpdate: (nextProps = {}, nextState = {}) => {
const props = this.props || {}, state = this.state || {};

if (Object.keys(props).length !== Object.keys(nextProps).length ||
Object.keys(state).length !== Object.keys(nextState).length) {
return true;
}

for (const key in nextProps) {
if (!is(props[key], nextProps[key])) {
return true;
}
}

for (const key in nextState) {
if (state[key] !== nextState[key] || !is(state[key], nextState[key])) {
return true;
}
}
return false;
}

4.單組件中拆分組件,盡量避免嵌套方法,一串傳參下去,導致一參數改變,一串函數都會重新render;

5.多組件同樣也應該避免從父級獲取store數據傳給子組件,會導致父組件中的子組件某個數據改變,導致父組件中所有子組件都會改變,全部重新render;

6. 未完待續。。。。


推薦閱讀:
相关文章