瞭解react
如何提高性能將有助於咱們更好的編寫代碼。我的認爲react
中不少的性能優化,其實都是圍繞着react
的核心diff
算法來展開的,經過優化,減小diff
算法中一些沒必要要的步驟,從而來提升性能。下面是我平時開發總結出來的一些經驗。react
通常來說,不少人都是使用的Component,可是會帶來一個問題,那就是在父組件的props
或者state
發生改變的時候,或者說的再準確一點,每當咱們調用setState
或者props
發生改變的時候,都會再執行一次render
,會從新生成virtualdom
,這個時候,好比說在該父組件中還存在Home
和Area
兩個組件,也會從新進行渲染(執行render -> 生成virtualDom -> 對比新舊virtualDom生成差別對象 -> 將生成的差別對象應用到真實dom上
),可是若是Home
和Area
沒有任何改變那麼差別對象不包含任何東西,那麼就至關於從新渲染的時候前三步耗費了性能。算法
這種性能耗費實際上是不必的, 因而react
提供了兩種方法給咱們來優化這個過程。redux
該方法在Component
中沒有繼承實現,須要咱們手動實現。而且在初次渲染和調用forceUpdate
的時候不觸發數組
若是咱們要判斷該組件是否更新必須經過比較this.props
和nextProps
加上this.state
與nextState
, 判斷他們是否改變,由於默認是返回true
的。若是改變了能夠經過返回true
來告訴react
,須要進行前面說到的從新渲染的過程,否者返回false
。那麼shouldComponentUpdate
後面的生命週期render() -> getSnapshotBeforeUpdate() -> componentDidUpdate()
都不會執行。這樣就達到了提升性能的做用。性能優化
可是須要注意的一點是,雖然該組件沒有被re-rendering
,可是若是該組件中還包含子組件,若是此時子組件的state
(這裏我沒有說還有props
)發生了改變,此子組件仍是會被從新渲染,並非說父組件的shouldComponentUpdate
返回false
,全部的子組件就不會從新渲染了。你可能如今會有點迷糊,不是說返回false
就不會從新渲染也不會執行render
的嗎? 那怎麼會從新渲染子組件呢? 讓咱們來想想:若是經過比較發現該組件的state
和props
都沒有發生改變,說明傳給子組件的props
確定沒有變,此時惟一可能改變的就是子組件自生的state
。在開發中可能會出現這種狀況,即父組件狀態沒有改變,可是子組件自身的state
發生改變。 並且在實際項目開發中,結合使用redux
來管理數據的時候,若是父組件的狀態沒有發生改變,子組件其實可能仍是會受到props
的影響的。數據結構
還有一點是在比較的過程當中,若是過於複雜也會致使過多的性能損耗,還不如不進行比較,進行默認的步驟。例如深度遍歷、JSON.stringify
這種耗費大量的性能的操做。一般比較推薦的作法是進行淺比較,而且下面要介紹的PureComponent
也是使用的淺比較,什麼是淺比較在下面PureComponent
專題中說。dom
PureComponent
內部幫咱們實現了shouldComponentUpdate
,其餘和Component
同樣。可是在shouldComponentUpdate
進行的是一個淺比較,看看官方文檔是怎麼說的。函數
他這裏並無仔細說淺比交究竟是怎麼比較。通過個人調試,我發現淺比較具備如下特色。性能
state
只比較第一層,而且若是是基本類型,那麼比較他們的值,若是是引用值,那麼比較他們的引用。props
, 也只比較第一層,規則和上面同樣。下面給幾個例子:假設state
中的數據結構是這樣優化
constructor (props) { super(props) this.state = { count: 0, person: { // name: 'longjincen' person: { person: { name: 'longjincen' } } } } }
當咱們繼承的是Component
的時候,只要調用setState
就會觸發從新渲染,可是當咱們繼承的是PureComponent
的時候,就不同了。當咱們像下面這樣改變state的時候
handleClick = () => { const { person } = this.state person.person.person = { name: 'xiaoya' } this.setState({ person: person }) }
這個時候最外層的person
始終都是同一個引用,因此這個時候是不會觸發從新渲染的,就算咱們內部的person
是一個新的引用,由於它只比較第一層。因此當咱們想要更新的時候,第一層返回的必須是一個新的對象,纔會觸發從新渲染。因此官方文檔中也說了,若是數據結構比較複雜,那麼可能會致使一些問題,要麼當你知道改變的時候調用forceUpdate
,要麼使用immutable
來包裝你的state
,immutable
是一個js
庫,用法和js
差很少,是facebook開發出來的,通常和PureComponent
搭配使用。
因此當咱們使用PureComponent
的時候返回的state必須是一個新的對象,下面給出幾種解決辦法:
1. handleClick = () => { const { person } = this.state person.person.person.name = 'xiaoya' this.setState({ person: Object.assign({}, person) }) } 2. handleClick = () => { const { person } = this.state person.person.person.name = 'xiaoya' this.setState({ person: { ...person } }) }
而對於接收到的先後的props
的比較和上面介紹的state
比較同樣,可是須要注意的一種狀況是,若是咱們將上面的第三層的person
對象傳給子組件,那麼在子組件中,因爲接收到的person
對象先後引用發生了改變,因此儘管父組件不會觸發從新渲染,子組件仍是會觸發,而若是咱們傳遞的是第二層或者第一層的person
因爲引用沒有改變,子組件也不會觸發從新渲染。
最後要說的是在實際項目開發中每每是使用PureComponent
與immutable
搭配的方式
無狀態組件就是使用定義函數的方式來定義組件,這種組件相比於使用類的方式來定義的組件(有狀態組件),少了不少初始化過程,更加精簡,因此要是可使用無狀態組件應當儘量的使用無狀態組件,會大幅度提高效率
render
中生成新的引用定義函數、使用內聯樣式或者動態生成一些不依賴state
或props
的jsx
這些,凡是不依賴state
或props
的都應該提到render
以外,不然會形成每次render
的時候生成新的引用,會致使在diff
算法對比屬性或節點的過程當中發現兩個引用不一致,對於react
來講,這說明屬性值發生了改變,最後會被替換成新的引用,形成性能浪費。例如函數應該放在render
函數外定義,樣式應該單獨建一個文件,而後引入,使用生命週期函數或者自定義函數動態生成一些不依賴state
或props
的jsx
。
state
在render
函數中,只能經過特定的用戶事件來觸發state
改變,否者容易形成不斷刷新的死循環,像下面這樣就是錯誤的
render () { this.setState({ name: 'xcacsa' }) }
避免使用數組下標做爲key
值, 應該確保惟一。在diff
算法中,key
值用來保證當列表中節點順序改變的時候節點的複用,而不是所有替換。