在React Native中使用組件來封裝界面模塊時(shí),整個(gè)界面就是一個(gè)大的組件,開發(fā)過程就是不斷優(yōu)化和拆分界面組件、構(gòu)造整個(gè)組件樹的過程。
所以學(xué)習(xí)理解組件的生命周期顯得尤為重要!
一、組件的屬性(props)和狀態(tài)(state)
1. 屬性(props)
它是組件的不可變屬性(組件自己不可以自己修改props,只可由其他組件調(diào)用它時(shí)在外部改變)。
組件自身定義了一組props作為對(duì)外提供的接口,展示一個(gè)組件時(shí)只需要指定props作為節(jié)點(diǎn)的屬性。
一般組件很少需要對(duì)外公開方法(例外:工具類的靜態(tài)方法等),唯一的交互途徑就是props。所以說它也是父組件與子組件通信的橋梁。
組件自己不可以自己修改props(即:props可認(rèn)為是只讀的),只可由其他組件調(diào)用它時(shí)在外部修改。
2. 狀態(tài)(state)
它是組件的內(nèi)部狀態(tài)屬性,主要用來存儲(chǔ)組件自身需要的數(shù)據(jù)。
除了初始化時(shí)可能由props來決定,之后就完全由組件自身去維護(hù)。
組件中由系統(tǒng)定義了setState方法,每次調(diào)用setState時(shí)都會(huì)更新組件的狀態(tài),觸發(fā)render方法重新渲染界面。
需要注意的是render方法是被異步調(diào)用的,這可以保證同步的多個(gè)setState方法只會(huì)觸發(fā)一次render,這樣做是有利于提高性能的。
二、組件的生命周期
對(duì)于自定義組件,除了必須實(shí)現(xiàn)的render方法,還有一些其他的可選方法可被調(diào)用。這些方法會(huì)在組件的不同時(shí)期之行,所以也可以說這些方法是組件的生命周期方法。
對(duì)于組件的生命周期來說一般分為四個(gè)階段,分別為:
創(chuàng)建階段、實(shí)例化階段、運(yùn)行(更新)階段、銷毀階段。
1. 創(chuàng)建階段
該階段主要發(fā)生在創(chuàng)建組件類的時(shí)候,在這個(gè)階段中會(huì)初始化組件的屬性類型和默認(rèn)屬性。
defaultProps / getDefaultProps()
這里會(huì)初始化一些默認(rèn)的屬性,通常會(huì)將固定的內(nèi)容放在這個(gè)過程中進(jìn)行初始化和賦值,一個(gè)控件可以利用this.props獲取在這里初始化它的屬性,由于組件初始化后,再次使用該組件不會(huì)調(diào)用getDefaultProps函數(shù),所以組件自己不可以自己修改props(即:props可認(rèn)為是只讀的),只可由其他組件調(diào)用它時(shí)在外部修改。
在ES5里,屬性類型和默認(rèn)屬性分別通過propTypes成員和getDefaultProps方法來實(shí)現(xiàn)。
//ES5
propTypes: {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
},
getDefaultProps: function() {? ? ? ??
? ? ? ? ? ? ? ?return {? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? autoPlay: false,? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?maxLoops: 10,? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?};
},
在ES6里,可以統(tǒng)一使用static成員來實(shí)現(xiàn).
//ES6
static?propTypes?=?{//用來指定props的類型
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? autoPlay:?React.PropTypes.bool.isRequired,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?maxLoops:?React.PropTypes.number.isRequired,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?posterFrameSrc:?React.PropTypes.string.isRequired,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? videoSrc:?React.PropTypes.string.isRequired,
};? //?注意這里有分號(hào)
static?defaultProps?=?{
? ? ? ? ? ? ? ? ? ? ? ? ? autoPlay:?false,
? ? ? ? ? ? ? ? ? ? ? ? ? ?maxLoops:?10,
};? //?注意這里有分號(hào)
2. 實(shí)例化階段
該階段主要發(fā)生在組件類被調(diào)用(實(shí)例化)的時(shí)候。
組件類被實(shí)例化的時(shí)候,觸發(fā)一系列流程:
1) constructor(props) / getInitialState()
這里是對(duì)控件的一些狀態(tài)進(jìn)行初始化,由于該函數(shù)不同于getDefaultProps,在以后的過程中,會(huì)再次調(diào)用,所以可以將控制控件的狀態(tài)的一些變量放在這里初始化,如控件上顯示的文字,可以通過this.state來獲取值,通過this.setState來修改state值。
在ES5里,通過getInitialState對(duì)狀態(tài)進(jìn)行初始化
getInitialState: function() {
? ? ? ? ? ? ? ? ? ? return {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loopsRemaining: this.props.maxLoops,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?};
},
在ES6里,通過constructor(構(gòu)造器)對(duì)狀態(tài)進(jìn)行初始化
constructor(props){
? ? ? ? super(props);
? ? ? ? this.state = {
? ? ? ? ? ? ? ? ?loopsRemaining: this.props.maxLoops,
? ? ? ? ? ? ? ? ? ? ? ? ? ? };
? ? }
2) componentWillMount()
準(zhǔn)備加載組件。
這個(gè)調(diào)用時(shí)機(jī)是在組件創(chuàng)建,并初始化了狀態(tài)之后,在第一次繪制 render() 之前??梢栽谶@里做一些業(yè)務(wù)初始化操作,也可以設(shè)置組件狀態(tài)。這個(gè)函數(shù)在整個(gè)生命周期中只被調(diào)用一次。
如果在這個(gè)函數(shù)里面調(diào)用setState,本次的render函數(shù)可以看到更新后的state,并且只渲染一次。
3) render()
render是一個(gè)組件必須有的方法,形式為一個(gè)函數(shù),渲染界面,并返回JSX或其他組件來構(gòu)成DOM,和Android的XML布局、WPF的XAML布局類似,只能返回一個(gè)頂級(jí)元素。
4) componentDidUpdate()
調(diào)用了render方法后,組件加載成功并被成功渲染出來以后所執(zhí)行的hook函數(shù),一般會(huì)將網(wǎng)絡(luò)請(qǐng)求等加載數(shù)據(jù)的操作,放在這個(gè)函數(shù)里進(jìn)行,來保證不會(huì)出現(xiàn)UI上的錯(cuò)誤。
3. 運(yùn)行(更新)階段
該階段主要發(fā)生在用戶操作之后,或者父組件有更新的時(shí)候,此時(shí)會(huì)根據(jù)用戶的操作行為,進(jìn)行相應(yīng)的界面結(jié)構(gòu)調(diào)整。
觸發(fā)的流程如下:
1) componentWillReceiveProps(nextProps)
當(dāng)組件接收到新的props時(shí),會(huì)觸發(fā)該函數(shù)。在該函數(shù)中,通??梢哉{(diào)用setState()來完成對(duì)state的修改。
輸入?yún)?shù) nextProps 是即將被設(shè)置的屬性,舊的屬性還是可以通過 this.props 來獲取。在這個(gè)回調(diào)函數(shù)里面,你可以根據(jù)屬性的變化,通過調(diào)用 this.setState() 來更新你的組件狀態(tài),這里調(diào)用更新狀態(tài)是安全的,并不會(huì)觸發(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)值。這個(gè)函數(shù)的返回值決定是否需要更新組件,如果 true 表示需要更新,繼續(xù)走后面的更新流程。否者,則不更新,直接進(jìn)入等待狀態(tài)。
默認(rèn)情況下,這個(gè)函數(shù)永遠(yuǎn)返回 true 用來保證數(shù)據(jù)變化的時(shí)候 UI 能夠同步更新。在大型項(xiàng)目中,你可以自己重載這個(gè)函數(shù),通過檢查變化前后屬性和狀態(tài),來決定 UI 是否需要更新,能有效提高應(yīng)用性能。
3) componentWillUpdate(nextProps, nextState)
shouldComponentUpdate返回true或者調(diào)用forceUpdate之后,就會(huì)開始準(zhǔn)更新組件,并調(diào)用 componentWillUpdate()。
輸入?yún)?shù)與 shouldComponentUpdate 一樣,在這個(gè)回調(diào)中,可以做一些在更新界面之前要做的事情。需要特別注意的是,在這個(gè)函數(shù)里面,你就不能使用 this.setState 來修改狀態(tài)。這個(gè)函數(shù)調(diào)用之后,就會(huì)把 nextProps 和 nextState 分別設(shè)置到 this.props 和 this.state 中。緊接著這個(gè)函數(shù),就會(huì)調(diào)用 render() 來更新界面了。
4) render()
再確定需要更新組件時(shí),調(diào)用render,根據(jù)diff算法,渲染界面,生成需要更新的虛擬DOM數(shù)據(jù)。
5) componentDidUpdate()
虛擬DOM同步到DOM中后,執(zhí)行該方法,可以在這個(gè)方法中做DOM操作。
除了首次render之后調(diào)用componentDidMount,其它render結(jié)束之后都是調(diào)用componentDidUpdate。
componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以對(duì)應(yīng)起來。區(qū)別在于,前者只有在掛載的時(shí)候會(huì)被調(diào)用;而后者在以后的每次更新渲染之后都會(huì)被調(diào)用。
ps:絕對(duì)不要在componentWillUpdate和componentDidUpdate中調(diào)用this.setState方法,否則將導(dǎo)致無限循環(huán)調(diào)用。
4. 銷毀階段
該階段主要發(fā)生組件銷亡的時(shí)候,觸發(fā)componentWillUnmount。當(dāng)組件需要從DOM中移除的時(shí)候,通常需要做一些取消事件綁定,移除虛擬DOM中對(duì)應(yīng)的組件數(shù)據(jù)結(jié)構(gòu),銷毀一些無效的定時(shí)器等工作,都可以在這個(gè)方法中處理。
componentWillUnmount()
當(dāng)組件要被從界面上移除的時(shí)候,就會(huì)調(diào)用 componentWillUnmount。
在這個(gè)函數(shù)中,可以做一些組件相關(guān)的清理工作,例如取消計(jì)時(shí)器、網(wǎng)絡(luò)請(qǐng)求等。
三、組件更新的方式(更新階段詳細(xì))
本來是沒有想要要詳細(xì)寫這部分內(nèi)容的,不過看到另一篇文章,寫得好好,就也放進(jìn)來詳細(xì)講下,感謝原文作者
參考自:http://www.itdecent.cn/p/4784216b8194里的更新方式部分
更新組件(重新渲染界面)的方式有以下四種:
首次渲染Initial Render,即首次加載組件
調(diào)用this.setState,狀態(tài)發(fā)生改變(并不是一次setState會(huì)觸發(fā)一次render,React可能會(huì)合并操作,再一次性進(jìn)行render)
父組件發(fā)生更新(一般就是props發(fā)生改變,但是就算props沒有改變或者父子組件之間沒有數(shù)據(jù)交換也會(huì)觸發(fā)render)
調(diào)用this.forceUpdate,強(qiáng)制更新
用圖來表示這四種方式如下:

四、總結(jié)
1. 組件生命周期總體流程圖

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 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 否