在React Native中使用組件來封裝界面模塊時(shí),整個(gè)界面就是一個(gè)大的組件,開發(fā)過程就是不斷優(yōu)化和拆分界面組件、構(gòu)造整個(gè)組件樹的過程。
所以學(xué)習(xí)理解組件的生命周期顯得尤為重要!
一、組件的屬性(props)和狀態(tài)(state)
1. 屬性(props)
它是組件的不可變屬性(組件自己不可以自己修改props)。
組件自身定義了一組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
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成員來實(shí)現(xiàn).
//ES6
static defaultProps = {
autoPlay: false,
maxLoops: 10,
}; // 注意這里有分號(hào)
static propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
}; // 注意這里有分號(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.lxweimin.com/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 |否
這篇文章參考了網(wǎng)上很多文章寫出來的,特別是結(jié)合ES6的不同點(diǎn)一起寫的。感覺還是挺有意義的,有不對(duì)的地方歡迎指出哈,歡迎大家提出建議。
其實(shí)如果是iOS開發(fā)人員,我覺得將組件的生命周期里的調(diào)用函數(shù)比較iOS的VC中的viewWillAppear等方法,還是挺容易理解的。(安卓應(yīng)該也會(huì)有對(duì)應(yīng)的概念才對(duì))
如果感覺看了還是不太熟悉,建議自己寫個(gè)demo,把所有方法都實(shí)現(xiàn)一次,控制臺(tái)打出對(duì)應(yīng)log,就一定可以更深刻的理解的!
正在寫React Native的學(xué)習(xí)教程ing,是一邊研究一邊編寫的,已有的成果如下(不斷更新哈,望鼓勵(lì)):
1) React Native 簡(jiǎn)介與入門
2) React Native 環(huán)境搭建和創(chuàng)建項(xiàng)目(Mac)
3) React Native 開發(fā)之IDE
4) React Native 入門項(xiàng)目與解析
5) React Native 相關(guān)JS和React基礎(chǔ)
6) React Native 組件生命周期(ES6)
7) React Native 集成到原生項(xiàng)目(iOS)
8) React Native 與原生之間的通信(iOS)
9) React Native 封裝原生UI組件(iOS)