在React Native中使用組件來封裝界面模塊時(shí)琉苇,整個(gè)界面就是一個(gè)大的組件,開發(fā)過程就是不斷優(yōu)化和拆分界面組件、構(gòu)造整個(gè)組件樹的過程柳畔。
所以學(xué)習(xí)理解組件的生命周期顯得尤為重要!
一郭赐、組件的屬性(props)和狀態(tài)(state)
1. 屬性(props)
它是組件的不可變屬性(組件自己不可以自己修改props)薪韩。
組件自身定義了一組props作為對外提供的接口,展示一個(gè)組件時(shí)只需要指定props作為節(jié)點(diǎn)的屬性捌锭。
一般組件很少需要對外公開方法(例外:工具類的靜態(tài)方法等)躬存,唯一的交互途徑就是props。所以說它也是父組件與子組件通信的橋梁舀锨。
組件自己不可以自己修改props(即:props可認(rèn)為是只讀的)岭洲,只可由其他組件調(diào)用它時(shí)在外部修改。
2. 狀態(tài)(state)
它是組件的內(nèi)部狀態(tài)屬性坎匿,主要用來存儲組件自身需要的數(shù)據(jù)盾剩。
除了初始化時(shí)可能由props來決定,之后就完全由組件自身去維護(hù)替蔬。
組件中由系統(tǒng)定義了setState方法告私,每次調(diào)用setState時(shí)都會更新組件的狀態(tài),觸發(fā)render方法重新渲染界面承桥。
需要注意的是render方法是被異步調(diào)用的驻粟,這可以保證同步的多個(gè)setState方法只會觸發(fā)一次render,這樣做是有利于提高性能的。
二蜀撑、組件的生命周期
對于自定義組件挤巡,除了必須實(shí)現(xiàn)的render方法,還有一些其他的可選方法可被調(diào)用酷麦。這些方法會在組件的不同時(shí)期之行矿卑,所以也可以說這些方法是組件的生命周期方法。
對于組件的生命周期來說一般分為四個(gè)階段沃饶,分別為:
**創(chuàng)建階段母廷、實(shí)例化階段、運(yùn)行(更新)階段糊肤、銷毀階段琴昆。 **
1. 創(chuàng)建階段
該階段主要發(fā)生在創(chuàng)建組件類的時(shí)候,在這個(gè)階段中會初始化組件的屬性類型和默認(rèn)屬性馆揉。
defaultProps / getDefaultProps()
這里會初始化一些默認(rèn)的屬性椎咧,通常會將固定的內(nèi)容放在這個(gè)過程中進(jìn)行初始化和賦值,一個(gè)控件可以利用this.props獲取在這里初始化它的屬性把介,由于組件初始化后勤讽,再次使用該組件不會調(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,
}; // 注意這里有分號
static propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
}; // 注意這里有分號
2. 實(shí)例化階段
該階段主要發(fā)生在組件類被調(diào)用(實(shí)例化)的時(shí)候。
組件類被實(shí)例化的時(shí)候君纫,觸發(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)備加載組件。
這個(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è)頂級元素存皂。
4) componentDidUpdate()
調(diào)用了render方法后,組件加載成功并被成功渲染出來以后所執(zhí)行的hook函數(shù)逢艘,一般會將網(wǎng)絡(luò)請求等加載數(shù)據(jù)的操作旦袋,放在這個(gè)函數(shù)里進(jìn)行,來保證不會出現(xiàn)UI上的錯(cuò)誤它改。
3. 運(yùn)行(更新)階段
該階段主要發(fā)生在用戶操作之后疤孕,或者父組件有更新的時(shí)候,此時(shí)會根據(jù)用戶的操作行為央拖,進(jìn)行相應(yīng)的界面結(jié)構(gòu)調(diào)整祭阀。
觸發(fā)的流程如下:
1) componentWillReceiveProps(nextProps)
當(dāng)組件接收到新的props時(shí),會觸發(fā)該函數(shù)鲜戒。在該函數(shù)中专控,通常可以調(diào)用setState()來完成對state的修改遏餐。
輸入?yún)?shù) nextProps 是即將被設(shè)置的屬性伦腐,舊的屬性還是可以通過 this.props 來獲取。在這個(gè)回調(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)值信粮。這個(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之后劫哼,就會開始準(zhǔn)更新組件,并調(diào)用 componentWillUpdate()割笙。
輸入?yún)?shù)與 shouldComponentUpdate 一樣权烧,在這個(gè)回調(diào)中,可以做一些在更新界面之前要做的事情伤溉。需要特別注意的是般码,在這個(gè)函數(shù)里面,你就不能使用 this.setState 來修改狀態(tài)乱顾。這個(gè)函數(shù)調(diào)用之后侈询,就會把 nextProps 和 nextState 分別設(shè)置到 this.props 和 this.state 中。緊接著這個(gè)函數(shù)糯耍,就會調(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可以對應(yīng)起來。區(qū)別在于背伴,前者只有在掛載的時(shí)候會被調(diào)用沸毁;而后者在以后的每次更新渲染之后都會被調(diào)用峰髓。
ps:絕對不要在componentWillUpdate和componentDidUpdate中調(diào)用this.setState方法,否則將導(dǎo)致無限循環(huán)調(diào)用息尺。
4. 銷毀階段
該階段主要發(fā)生組件銷亡的時(shí)候携兵,觸發(fā)componentWillUnmount。當(dāng)組件需要從DOM中移除的時(shí)候搂誉,通常需要做一些取消事件綁定徐紧,移除虛擬DOM中對應(yīng)的組件數(shù)據(jù)結(jié)構(gòu),銷毀一些無效的定時(shí)器等工作炭懊,都可以在這個(gè)方法中處理并级。
componentWillUnmount()
當(dāng)組件要被從界面上移除的時(shí)候,就會調(diào)用 componentWillUnmount凛虽。
在這個(gè)函數(shù)中死遭,可以做一些組件相關(guān)的清理工作广恢,例如取消計(jì)時(shí)器凯旋、網(wǎng)絡(luò)請求等。
三钉迷、組件更新的方式(更新階段詳細(xì))
本來是沒有想要要詳細(xì)寫這部分內(nèi)容的至非,不過看到另一篇文章,寫得好好糠聪,就也放進(jìn)來詳細(xì)講下荒椭,感謝原文作者
參考自:http://www.reibang.com/p/4784216b8194 里的更新方式部分
更新組件(重新渲染界面)的方式有以下四種:
- 首次渲染Initial Render,即首次加載組件
- 調(diào)用this.setState舰蟆,狀態(tài)發(fā)生改變(并不是一次setState會觸發(fā)一次render趣惠,React可能會合并操作,再一次性進(jìn)行render)
- 父組件發(fā)生更新(一般就是props發(fā)生改變身害,但是就算props沒有改變或者父子組件之間沒有數(shù)據(jù)交換也會觸發(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)一起寫的侍瑟。感覺還是挺有意義的,有不對的地方歡迎指出哈丙猬,歡迎大家提出建議涨颜。
其實(shí)如果是iOS開發(fā)人員,我覺得將組件的生命周期里的調(diào)用函數(shù)比較iOS的VC中的viewWillAppear等方法茧球,還是挺容易理解的庭瑰。(安卓應(yīng)該也會有對應(yīng)的概念才對)
如果感覺看了還是不太熟悉,建議自己寫個(gè)demo抢埋,把所有方法都實(shí)現(xiàn)一次见擦,控制臺打出對應(yīng)log钉汗,就一定可以更深刻的理解的!
正在寫React Native的學(xué)習(xí)教程ing鲤屡,是一邊研究一邊編寫的损痰,已有的成果如下(不斷更新哈,望鼓勵(lì)):
1) React Native 簡介與入門
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)