6) React Native 組件生命周期(ES6)

在React Native中使用組件來封裝界面模塊時,整個界面就是一個大的組件,開發(fā)過程就是不斷優(yōu)化和拆分界面組件、構(gòu)造整個組件樹的過程。
所以學(xué)習(xí)理解組件的生命周期顯得尤為重要!

一、組件的屬性(props)和狀態(tài)(state)

1. 屬性(props)

它是組件的不可變屬性(組件自己不可以自己修改props)。
組件自身定義了一組props作為對外提供的接口,展示一個組件時只需要指定props作為節(jié)點的屬性。
一般組件很少需要對外公開方法(例外:工具類的靜態(tài)方法等),唯一的交互途徑就是props。所以說它也是父組件與子組件通信的橋梁
組件自己不可以自己修改props(即:props可認(rèn)為是只讀的),只可由其他組件調(diào)用它時在外部修改。

2. 狀態(tài)(state)

它是組件的內(nèi)部狀態(tài)屬性,主要用來存儲組件自身需要的數(shù)據(jù)。
除了初始化時可能由props來決定,之后就完全由組件自身去維護(hù)。
組件中由系統(tǒng)定義了setState方法,每次調(diào)用setState時都會更新組件的狀態(tài),觸發(fā)render方法重新渲染界面
需要注意的是render方法是被異步調(diào)用的,這可以保證同步的多個setState方法只會觸發(fā)一次render,這樣做是有利于提高性能的。

二、組件的生命周期

對于自定義組件,除了必須實現(xiàn)的render方法,還有一些其他的可選方法可被調(diào)用。這些方法會在組件的不同時期之行,所以也可以說這些方法是組件的生命周期方法。
對于組件的生命周期來說一般分為四個階段,分別為:
**創(chuàng)建階段、實例化階段、運(yùn)行(更新)階段、銷毀階段。 **

1. 創(chuàng)建階段

該階段主要發(fā)生在創(chuàng)建組件類的時候,在這個階段中會初始化組件的屬性類型和默認(rèn)屬性。

defaultProps / getDefaultProps()

這里會初始化一些默認(rèn)的屬性,通常會將固定的內(nèi)容放在這個過程中進(jìn)行初始化和賦值,一個控件可以利用this.props獲取在這里初始化它的屬性,由于組件初始化后,再次使用該組件不會調(diào)用getDefaultProps函數(shù),所以組件自己不可以自己修改props(即:props可認(rèn)為是只讀的),只可由其他組件調(diào)用它時在外部修改。

在ES5里,屬性類型和默認(rèn)屬性分別通過propTypes成員和getDefaultProps方法來實現(xiàn)。

//ES5
getDefaultProps: function() {
        return {
            autoPlay: false,
            maxLoops: 10,
        };
},
propTypes: {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
},

在ES6里,可以統(tǒng)一使用static成員來實現(xiàn).

//ES6
static defaultProps = {
        autoPlay: false,
        maxLoops: 10,
};  // 注意這里有分號
static propTypes = {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
};  // 注意這里有分號

2. 實例化階段

該階段主要發(fā)生在組件類被調(diào)用(實例化)的時候。
組件類被實例化的時候,觸發(fā)一系列流程:

1) constructor(props) / getInitialState()

這里是對控件的一些狀態(tài)進(jìn)行初始化,由于該函數(shù)不同于getDefaultProps,在以后的過程中,會再次調(diào)用,所以可以將控制控件的狀態(tài)的一些變量放在這里初始化,如控件上顯示的文字,可以通過this.state來獲取值,通過this.setState來修改state值。

在ES5里,通過getInitialState對狀態(tài)進(jìn)行初始化

getInitialState: function() {
        return {
            loopsRemaining: this.props.maxLoops,
        };
},

在ES6里,通過constructor(構(gòu)造器)對狀態(tài)進(jìn)行初始化

constructor(props){
        super(props);
        this.state = {
            loopsRemaining: this.props.maxLoops,
        };
}
2) componentWillMount()

準(zhǔn)備加載組件。
這個調(diào)用時機(jī)是在組件創(chuàng)建,并初始化了狀態(tài)之后,在第一次繪制 render() 之前。可以在這里做一些業(yè)務(wù)初始化操作,也可以設(shè)置組件狀態(tài)。這個函數(shù)在整個生命周期中只被調(diào)用一次。
如果在這個函數(shù)里面調(diào)用setState,本次的render函數(shù)可以看到更新后的state,并且只渲染一次。

3) render()

render是一個組件必須有的方法,形式為一個函數(shù),渲染界面,并返回JSX或其他組件來構(gòu)成DOM,和Android的XML布局、WPF的XAML布局類似,只能返回一個頂級元素

4) componentDidUpdate()

調(diào)用了render方法后,組件加載成功并被成功渲染出來以后所執(zhí)行的hook函數(shù),一般會將網(wǎng)絡(luò)請求等加載數(shù)據(jù)的操作,放在這個函數(shù)里進(jìn)行,來保證不會出現(xiàn)UI上的錯誤

3. 運(yùn)行(更新)階段

該階段主要發(fā)生在用戶操作之后,或者父組件有更新的時候,此時會根據(jù)用戶的操作行為,進(jìn)行相應(yīng)的界面結(jié)構(gòu)調(diào)整。
觸發(fā)的流程如下:

1) componentWillReceiveProps(nextProps)

當(dāng)組件接收到新的props時,會觸發(fā)該函數(shù)。在該函數(shù)中,通常可以調(diào)用setState()來完成對state的修改
輸入?yún)?shù) nextProps 是即將被設(shè)置的屬性,舊的屬性還是可以通過 this.props 來獲取。在這個回調(diào)函數(shù)里面,你可以根據(jù)屬性的變化,通過調(diào)用 this.setState() 來更新你的組件狀態(tài),這里調(diào)用更新狀態(tài)是安全的,并不會觸發(fā)額外的 render() 調(diào)用。如下:

componentWillReceiveProps: function(nextProps) {  
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}
2) shouldComponentUpdate(nextProps, nextState)

返回布爾值(決定是否需要更新組件)

輸入?yún)?shù) nextProps 和上面的 componentWillReceiveProps 函數(shù)一樣,nextState 表示組件即將更新的狀態(tài)值。這個函數(shù)的返回值決定是否需要更新組件,如果 true 表示需要更新,繼續(xù)走后面的更新流程。否者,則不更新,直接進(jìn)入等待狀態(tài)。
默認(rèn)情況下,這個函數(shù)永遠(yuǎn)返回 true 用來保證數(shù)據(jù)變化的時候 UI 能夠同步更新。在大型項目中,你可以自己重載這個函數(shù),通過檢查變化前后屬性和狀態(tài),來決定 UI 是否需要更新,能有效提高應(yīng)用性能。

3) componentWillUpdate(nextProps, nextState)

shouldComponentUpdate返回true或者調(diào)用forceUpdate之后,就會開始準(zhǔn)更新組件,并調(diào)用 componentWillUpdate()。
輸入?yún)?shù)與 shouldComponentUpdate 一樣,在這個回調(diào)中,可以做一些在更新界面之前要做的事情。需要特別注意的是,在這個函數(shù)里面,你就不能使用 this.setState 來修改狀態(tài)。這個函數(shù)調(diào)用之后,就會把 nextProps 和 nextState 分別設(shè)置到 this.props 和 this.state 中。緊接著這個函數(shù),就會調(diào)用 render() 來更新界面了。

4) render()

再確定需要更新組件時,調(diào)用render,根據(jù)diff算法,渲染界面,生成需要更新的虛擬DOM數(shù)據(jù)。

5) componentDidUpdate()

虛擬DOM同步到DOM中后,執(zhí)行該方法,可以在這個方法中做DOM操作。
除了首次render之后調(diào)用componentDidMount,其它render結(jié)束之后都是調(diào)用componentDidUpdate。

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以對應(yīng)起來。區(qū)別在于,前者只有在掛載的時候會被調(diào)用;而后者在以后的每次更新渲染之后都會被調(diào)用。
ps:絕對不要在componentWillUpdate和componentDidUpdate中調(diào)用this.setState方法,否則將導(dǎo)致無限循環(huán)調(diào)用。

4. 銷毀階段

該階段主要發(fā)生組件銷亡的時候,觸發(fā)componentWillUnmount。當(dāng)組件需要從DOM中移除的時候,通常需要做一些取消事件綁定,移除虛擬DOM中對應(yīng)的組件數(shù)據(jù)結(jié)構(gòu),銷毀一些無效的定時器等工作,都可以在這個方法中處理。

componentWillUnmount()

當(dāng)組件要被從界面上移除的時候,就會調(diào)用 componentWillUnmount。
在這個函數(shù)中,可以做一些組件相關(guān)的清理工作,例如取消計時器、網(wǎng)絡(luò)請求等

三、組件更新的方式(更新階段詳細(xì))

本來是沒有想要要詳細(xì)寫這部分內(nèi)容的,不過看到另一篇文章,寫得好好,就也放進(jìn)來詳細(xì)講下,感謝原文作者
參考自:http://www.lxweimin.com/p/4784216b8194 里的更新方式部分

更新組件(重新渲染界面)的方式有以下四種

  1. 首次渲染Initial Render,即首次加載組件
  2. 調(diào)用this.setState,狀態(tài)發(fā)生改變(并不是一次setState會觸發(fā)一次render,React可能會合并操作,再一次性進(jìn)行render)
  3. 父組件發(fā)生更新(一般就是props發(fā)生改變,但是就算props沒有改變或者父子組件之間沒有數(shù)據(jù)交換也會觸發(fā)render)
  4. 調(diào)用this.forceUpdate,強(qiáng)制更新

用圖來表示這四種方式如下:


更新組件方式.png

四、總結(jié)

1. 組件生命周期總體流程圖

組件的生命周期.png

2. 生命周期的回調(diào)函數(shù)總結(jié)

|生命周期 |調(diào)用次數(shù) |能否使用 setSate() |
|: ------:|:------:|:------:|
|defaultProps / getDefaultProps| 1(全局調(diào)用一次) | 否
|constructor / getInitialState |1 |否
|componentWillMount |1 |
|render |>=1 |否
|componentDidMount |1 |
|componentWillReceiveProps |>=0 |
|shouldComponentUpdate |>=0 |否
|componentWillUpdate |>=0 |否
|componentDidUpdate |>=0 |否
|componentWillUnmount |1 |否


這篇文章參考了網(wǎng)上很多文章寫出來的,特別是結(jié)合ES6的不同點一起寫的。感覺還是挺有意義的,有不對的地方歡迎指出哈,歡迎大家提出建議。
其實如果是iOS開發(fā)人員,我覺得將組件的生命周期里的調(diào)用函數(shù)比較iOS的VC中的viewWillAppear等方法,還是挺容易理解的。(安卓應(yīng)該也會有對應(yīng)的概念才對)
如果感覺看了還是不太熟悉,建議自己寫個demo,把所有方法都實現(xiàn)一次,控制臺打出對應(yīng)log,就一定可以更深刻的理解的!

正在寫React Native的學(xué)習(xí)教程ing,是一邊研究一邊編寫的,已有的成果如下(不斷更新哈,望鼓勵):
1) React Native 簡介與入門
2) React Native 環(huán)境搭建和創(chuàng)建項目(Mac)
3) React Native 開發(fā)之IDE

4) React Native 入門項目與解析
5) React Native 相關(guān)JS和React基礎(chǔ)
6) React Native 組件生命周期(ES6)
7) React Native 集成到原生項目(iOS)
8) React Native 與原生之間的通信(iOS)
9) React Native 封裝原生UI組件(iOS)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容