React生命週期shouldComponentUpdate介紹及性能優化
在react開發中,經常需要對數據state狀態進行改變,但是這種方式每當setState的時候都會將所有的組件重新渲染一遍,這樣就會有重複渲染render的問題。
看如下圖組件樹:
默認情況下,當執行setState()方法時,react 會重新渲染整個組件樹,這造成不必要的性能開銷。
黃色的節點表示我們修改了數據的節點,我們希望只重新渲染這個部分,而其它藍色的節點是我們不希望重複渲染的。那麼怎麼解決呢?生命週期的shouldComponentUpdate就可以簡單解決。
shouldComponentUpdate介紹
shouldComponentUpdate(nextProps, nextState)
- nextProps: 表示下一個props。
- nextState: 表示下一個state的值。
官方API解釋道根據 shouldComponentUpdate() 的返回值,判斷 React 組件的輸出是否受當前 state 或 props 更改的影響。默認行爲是 state 每次發生變化組件都會重新渲染。大部分情況下,你應該遵循默認行爲。
shouldComponentUpdate(nextProps, nextState){
//組件是否需要更新,需要返回一個布爾值,返回true則更新,返回flase不更新,這是一個關鍵點
console.log('shouldComponentUpdate組件是否應該更新,需要返回布爾值',nextProps, nextState)
return true
}
當 props 或 state 發生變化時,shouldComponentUpdate() 會在渲染執行之前被調用。返回值默認爲 true。首次渲染或使用 forceUpdate() 時不會調用該方法。
使用shouldComponentUpdate性能優化
React中props,state值的變化,會導致組件重新渲染。使用shouldComponentUpdate就是爲了減少render不必要的渲染。 返回布爾值,然後做 Virtual DOM 比較,並得出是否需要做真實 DOM 更新,儘管React的虛擬算法複雜度已經有了很多優化,但是在大規模組件更新時,依然會是個不必要的損耗。會帶來很多無必要的渲染併成爲性能瓶頸。接下來使用來這個生命週期來解決吧
例子:
import React, { Component } from 'react'
class Title extends Component {
shouldComponentUpdate(nextProps){
return nextProps.title!==this.props.title
}
render() {
console.log("我是title組件")
return (
<div>
標題:{this.props.title}
</div>
)
}
}
class Count extends Component {
render() {
console.log("我是條數組件")
return (
<div>
條數:{this.props.count}
</div>
)
}
}
export default class Purememo extends Component {
constructor(props){
super(props)
this.state={
title:'shouldComponentUpdate使用',
count:0
}
}
componentDidMount(){
setInterval(()=>{
this.setState({
count:this.state.count+1
})
},1000)
}
render() {
return (
<div>
<Title title={this.state.title}></Title>
<Count count={this.state.count}></Count>
</div>
)
}
}
Purememo整個組件會重新渲染,如果不適用shouldComponentUpdate那麼Title,Count會一直渲染,但是使用shouldComponentUpdate後Title則不會發生渲染。
當然你也可以考慮使用內置的 React.PureComponent 組件,shouldComponentUpdate()。PureComponent 會對 props 和 state 進行淺層比較,並減少了跳過必要更新的可能性。不建議shouldComponentUpdate() 中進行深層比較或使用 JSON.stringify()。這樣非常影響效率,且會損害性能。
面對複雜的對象時,這樣的數據就沒有效果了,因爲在js中,object,array,function屬於引用類型,即使改變其中數據,他們指向的還是同一內存地址,所以採用上面的判斷就不行了。例如:
obj: {
age: 12,
name: 'xiaoming',
student: {
count: 1
}
}
解決方法有以下幾種:
- 使用setState改變數據之前,先採用es6中assgin進行拷貝,但是assgin只深拷貝的數據的第一層,所以說不是最完美的解決辦法。
const o2 = Object.assign({},this.state.obj)
o2.student.count = '00000';
this.setState({
obj: o2,
})
- 使用JSON.parse(JSON.stringfy())進行深拷貝,但是遇到數據爲undefined和函數時就會錯。
const o2 = JSON.parse(JSON.stringify(this.state.obj))
o2.student.count = '00000';
this.setState({
obj: o2,
})
- 使用react官方推薦的第三方庫:immutable.js進行項目的搭建。immutable中講究數據的不可變性,每次對數據進行操作前,都會自動的對數據進行深拷貝,項目中數據採用immutable的方式,可以輕鬆解決問題,但是又多了一套API去學習。
關於immutable.js可參考
精讀 Immutable 結構共享
深入探究Immutable.js的實現機制(一)
深入探究Immutable.js的實現機制(二)
瞭解Clojure的持久變量
部分參考他人,侵刪