React 渲染 和 生命週期

啊就是我發表於2020-01-16

React渲染機制

觸發Render

Reac更新檢視必須觸發Render,但是這樣往往又很影響效能(因為有dom改動),React利用特有的diff演算法採用虛擬DOM,是的效能有了很大的提升。

但是我們在開發中還是要注意效能優化,避免沒有意義的Render。

觸發Render的條件:

  • 首次載入元件
  • 使用了setState(更新了Props)
  • Props更新了(父級傳給子級的值改變了)

很多情況下我們可以直接避免很多2、3情況導致的效能問題。

生命週期執行順序

只執行一次: constructor、componentWillMount、componentDidMount

執行多次:render 、子元件的componentWillReceiveProps、componentWillUpdate、componentDidUpdate

有條件的執行:componentWillUnmount(頁面離開,元件銷燬時)

不執行的:根元件(ReactDOM.render在DOM上的元件)的componentWillReceiveProps(因為沒有父元件給傳遞props)

//

componentWillMount 在渲染前呼叫,在客戶端也在服務端。

componentDidMount : 在第一次渲染後呼叫,只在客戶端。之後元件已經生成了對應的DOM結構,可以通過this.getDOMNode()來進行訪問。
componentWillReceiveProps 在元件接收到一個新的 prop (更新後)時被呼叫。這個方法在初始化render時不會被呼叫。

shouldComponentUpdate 返回一個布林值。在元件接收到新的props或者state時被呼叫。在初始化時或者使用forceUpdate時不被呼叫。 
可以在你確認不需要更新元件時使用。

componentWillUpdate在元件接收到新的props或者state但還沒有render時被呼叫。在初始化時不會被呼叫。

componentDidUpdate 在元件完成更新後立即呼叫。在初始化時不會被呼叫。

componentWillUnmount在元件從 DOM 中移除的時候立刻被呼叫。
複製程式碼

React Diff

雖然React高效的diff演算法和虛擬dom完美結合,讓使用者可以‘無限制’重新整理而且不用考慮效能問題,但是diff演算法還是需要很多時間,如果是很大的模組,不注意Render觸發的細節,自然還是很影響效能。

沒有優化的例子

功能如下:

  • 更新state的值,但是需要呼叫setState方法
  • 傳給後代元件的值不改變,即為後代props沒有變化
// 父
import React from 'react'
import MockChild from './child/index.js'
export default class Demo5 extends React.Component{
    constructor(args){
        super(args)
        
        this.state = {
            'mockNum': 0
        }
    }
    handleClick(){
        this.setState({
            'mockNum': 0,
        })
    }
    render(){
        console.log('父級state ==============>', this.state)
        return(
            <div>
                <button onClick={this.handleClick.bind(this)}>點選</button>
                <MockChild  mockNum={this.state.mockNum}/>
            </div>
        )
    }
}

//子
render(){
    console.log('子級Props =============>', this.props)
    return(
        <div></div>
    )
}
複製程式碼

我們反覆觸發事件,雖然state的值並沒有任何變化,但是我們看列印的結果:

React 渲染 和 生命週期

render 重複執行

可能會疑問,我在專案並不會做這麼一種無用功的!但實際上,當一個元件邏輯複雜起來之後,會產生各種各樣的情況.比如:

比如一個元件的中有個包含onChange事件的input,當我改變該輸入框內容的時候呼叫了setState,渲染檢視的時候也會觸發子元件的重新render.但我們明明沒有做任何和子元件有聯絡的操作

例子:

//父
state = {
    'mockValue': '123'    
}
handleChange(e){
    this.setState({
        'value': '123',
    })
}

//render
<input onChange={this.handleChange.bind(this)} />
<MockChild />

/*
* 子元件不做變化
*/
複製程式碼

React 渲染 和 生命週期

生命週期基礎優化

shouldComponentUpdate

執行過程:

React 渲染 和 生命週期

它是在render渲染之前觸發的,只要在這裡加以判斷就可以有效阻止 '無用'的render 觸發。componentWillReceiveProps也可以,但是它只有props的更新判斷,而有時候也不能放過未更改的state!

shouldComponentUpdate(nextProps, nextState){
    if(nextState !== this.state && nextProps !== this.props) return true;
    return false
}
複製程式碼

這樣可以有效的解決冗餘的Render,當專案很大的時候,就需要更準確的判斷到某一個值

shouldComponentUpdate(nextProps, nextState){
    if(nextState.xxx !== this.state.xxx && nextProps.xxx !== this.props.xxx) return true;
    return false
}
複製程式碼

componentWillReceiveProps

幾種情況:

1、componentWillReceiveProps函式有一個引數nextProps,它是一個 { 物件 } ,它是update時候(下一次)父元件傳遞過來的props。

2、有些生命週期函式只執行一次,而有的執行多次,其中componentWillReceiveProps執行多次,而constructor等執行一次。

3、還需知道在子元件中每次傳遞過來的this.props物件其實和componentWillReceiveProps的nextProps是一樣的,都是最新的。

4、componentWillReceiveProps生命週期是在更新子元件最先執行的,優先於compoentWillUpdate,更優先於render。

5、render函式裡不能使用setState(),否則會造成死迴圈。
複製程式碼
大部分情況下 componentWillReceiveProps 生命週期函式是沒用的,即可以略去不寫,

但是在constructor函式中初始化了某個state,必須用 componentWillReceiveProps 來更新state,不可省去,否則render中的state將得不到更新。
同時如果您想在子元件監聽watch值變化做處理,也可以用到componentWillReceiveProps

使用componentWillReceiveProps的時候,不要去向上分發,呼叫父元件的相關setState方法,否則會成為死迴圈。
複製程式碼

參考文章

1、www.cnblogs.com/soyxiaobi/p… 【React的渲染機制】

2、www.cnblogs.com/soyxiaobi/p… 【React生命週期執行】

相關文章