前言
為了進(jìn)一步的了解React的工作過(guò)程,已經(jīng)曉得了怎么編寫React組件,知道了React的數(shù)據(jù)流,那么是時(shí)候?qū)W習(xí)React組件的生命周期了,每個(gè)組件都包含生命周期方法,生命周期如同四季更替,一個(gè)人的生,老,病,死.在每個(gè)特殊的年齡階段,做這不同的事情
在React編寫組件中,同樣,每個(gè)組件在網(wǎng)頁(yè)中都有被創(chuàng)建,更新,刪除這么一過(guò)程,就像有機(jī)的生命體一樣
理解生命周期函數(shù)對(duì)于編寫React組件代碼是非常重要的
如果你不清楚生命周期,以及生命周期的應(yīng)用場(chǎng)景,那么本篇就是你想要知道的
生命周期(鉤子)函數(shù)
定義: 在特定的階段,能夠自動(dòng)執(zhí)行的函數(shù)(方法)
在前面的JSX學(xué)習(xí)中,一個(gè)React元素渲染到頁(yè)面當(dāng)中,本質(zhì)上是通過(guò)底層的React.CreateElement的一個(gè)方法實(shí)現(xiàn)的,它是一個(gè)javascript對(duì)象,將虛擬DOM轉(zhuǎn)化為真實(shí)的DOM,最后通過(guò)ReactDOM.render()方法將真實(shí)的DOM渲染掛載到對(duì)應(yīng)的頁(yè)面位置上
一個(gè)組件的渲染,經(jīng)歷了以下幾個(gè)過(guò)程:可以對(duì)照這個(gè)生命周期圖譜的
-
組件的裝載(Mount):把組件第一次在DOM樹(shù)中渲染的過(guò)程
- componentWillMount:組件即將被掛載,在Render方法之前調(diào)用:
- 應(yīng)用場(chǎng)景: 常用于組件的啟動(dòng)工作,例如:Ajax數(shù)據(jù)的獲取,定時(shí)器的啟動(dòng),類似Render函數(shù)的前哨,調(diào)用setState修改狀態(tài)也不會(huì)引起重新繪制,這個(gè)時(shí)候沒(méi)有任何渲染,需要注意的是,它可以在服務(wù)器端被調(diào)用,也可以在瀏覽器端調(diào)用
-
componentDidMount:組件被加載完之后調(diào)用,也就是render函數(shù)執(zhí)行之后調(diào)用一姿,相當(dāng)于render函數(shù)的后衛(wèi),當(dāng)這個(gè)生命周期執(zhí)行時(shí),render函數(shù)會(huì)引發(fā)渲染,組件重新掛載到DOM樹(shù)上,注意它只能在瀏覽器端調(diào)用,在服務(wù)器端使用React的時(shí)候不會(huì)調(diào)用,裝載是將組件渲染,并且構(gòu)造DOM元素,然后塞入頁(yè)面的過(guò)程,這個(gè)狀態(tài)是不可能在服務(wù)器端完成的,服務(wù)器端不可能產(chǎn)生DOM樹(shù)的
應(yīng)用場(chǎng)景:我們往往在這個(gè)生命周期內(nèi)進(jìn)行Ajax的獲取,填充組件的內(nèi)容,因?yàn)樵赾omponentDidMount被調(diào)用時(shí),組件已經(jīng)掛載到DOM樹(shù)上了,而往往若需要結(jié)合第三方庫(kù)的使用,例如:JQ等,也是放到這個(gè)生命周期函數(shù)中進(jìn)行處理的 -
getSnapshotBeforeUpdate(prevProps, prevState):
使用場(chǎng)景:該函數(shù)在最終render結(jié)果提交到DOM之前被調(diào)用,記錄DOM刷新前的特性,如:滾動(dòng)位置
注意:該函數(shù)的返回值會(huì)作為參數(shù)傳遞給ComponentDidUpdate - componentWillUnmount: 當(dāng)組件對(duì)應(yīng)的 DOM 元素從頁(yè)面中刪除之前調(diào)用
-
組件的更新(update): 當(dāng)組件被重新渲染的過(guò)程(state與props發(fā)生改變都會(huì)引起渲染)
- componentWillReceiveProps:
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
-
組件的卸載(unmount): 組件從DOM中刪除的過(guò)程
- componentWillUnmount: 組件從頁(yè)面銷毀時(shí),會(huì)觸發(fā)該函數(shù),當(dāng)需要對(duì)數(shù)據(jù)進(jìn)行清理時(shí),例如定時(shí)器的清理,放到該函數(shù)里面去做
三種不同的過(guò)程,React庫(kù)會(huì)依次調(diào)用組件的一些成員函數(shù)(生命周期函數(shù))
裝載過(guò)程
當(dāng)組件第一次被渲染的時(shí)候,會(huì)依次的調(diào)用如下生命周期函數(shù)
- constructor:構(gòu)造器函數(shù)
- getDerivedStateFromProps(props,state):
使用場(chǎng)景:當(dāng)組件內(nèi)部的state變化依賴于props時(shí),調(diào)用該生命周期函數(shù)
注意:不要過(guò)度使用該函數(shù),如果你的操作依賴于props的更改并有副作用墓造,最好放到componentDidUpdate中 - componentWillMount:組件掛載開(kāi)始之前調(diào)用,也就是render函數(shù)之前被自動(dòng)調(diào)用,在React16.3版本之后不應(yīng)該使用,由于該函數(shù)在Render函數(shù)之前調(diào)用,因此使用同步的setState方法不會(huì)觸發(fā)額外的render處理
它也只會(huì)在初始化的時(shí)候調(diào)用一次,所以this壞境的綁定放在這里面也是可以的,但是最好是放在constructor構(gòu)造器函數(shù)里面,如果是處理異步操作或者有副作用的訂閱事件處理,例如:Ajax數(shù)據(jù)獲取,則放到componentDidMount中
- render:組件的渲染,插入到DOM元素中,
- componentDidMount:組件掛載完之后調(diào)用,也就是在render函數(shù)之后調(diào)用,DOM已經(jīng)插入到頁(yè)面中了的,可以在這里使用refs
constructor:構(gòu)造器函數(shù)
constructor(ptops) {
super(props); // 一定要調(diào)用super,并且接收props參數(shù),否則該組件的實(shí)例方法無(wú)法獲取到外部的props值
}
至于constructor在上節(jié)當(dāng)中已經(jīng)提及過(guò),并不是每個(gè)組件都需要定義constructor構(gòu)造器函數(shù)溜歪,函數(shù)式(無(wú)狀態(tài))組件就不需要定義構(gòu)造函數(shù)
一般使用constructor構(gòu)造函數(shù)有如下兩種情況
- 組件內(nèi)部初始化state,因?yàn)樯芷趦?nèi)的任何函數(shù)都可能要訪問(wèn)state,取它的值,進(jìn)行相應(yīng)的邏輯處理,它是該組件一個(gè)私有的對(duì)象變量
- 在對(duì)JSX元素上綁定事件監(jiān)聽(tīng)處理函數(shù)時(shí),也就是組件內(nèi)部成員函數(shù)(方法)this壞境的綁定,因?yàn)樵贓s6中類的成員方法在執(zhí)行時(shí)this并不會(huì)和類的實(shí)例化本身自動(dòng)的綁定,你需要手動(dòng)bind的方式進(jìn)行綁定
為了方便調(diào)用,在構(gòu)造函數(shù)中,this就是當(dāng)前組件的實(shí)例,往往在構(gòu)造函數(shù)中將組件實(shí)例下的成員方法綁定this為當(dāng)前的實(shí)例對(duì)象
constructor(props){
super(props);
}
this.handleBtnClick = this.handleBtnCLick.bind(this);
this.handleInputChange = this.handleInputChange.bind(this)
在執(zhí)行了constructor構(gòu)造器函數(shù)后,執(zhí)行componentWillMount方法,然后在執(zhí)行render方法,執(zhí)行完render方法后,在執(zhí)行componentDidMount方法,整個(gè)裝載過(guò)程就結(jié)束了的
當(dāng)然這其中的一個(gè)componentWillUnmount方法是在組件銷毀前進(jìn)行觸發(fā),也就是刪除DOM元素之前調(diào)用,這個(gè)常用于當(dāng)組件從頁(yè)面刪除銷毀時(shí),做一些數(shù)據(jù)清理的時(shí)候能用得上,例如定時(shí)器的清理,取消網(wǎng)絡(luò)請(qǐng)求,在該生命周期函數(shù)內(nèi),不應(yīng)該調(diào)用setState函數(shù),因?yàn)樵摻M件銷毀后,將不會(huì)被重新渲染
具體的實(shí)例代碼如下所示:
import React, { Fragment, Component } from 'react';
import ReactDOM from 'react-dom';
class LifeCycle extends Component {
constructor(props){
super(props);
console.log("1-constructor函數(shù)被調(diào)用執(zhí)行");
this.state = {
isShow: true
}
this.handleBtnClick = this.handleBtnClick.bind(this);
}
componentWillMount(){
console.log("2-componentWillMount函數(shù)已執(zhí)行,組件掛載之前,在render方法之前調(diào)用", this.state.isShow);
}
componentDidMount() {
console.log("4-componentDidMount函數(shù)已執(zhí)行,組件掛載完之后,DOM元素已經(jīng)插入到頁(yè)面后調(diào)用");
}
render(){
console.log("3-render函數(shù)執(zhí)行");
return (
<Fragment>
<div>
{ this.state.isShow? <Text />:""}
<button onClick={ this.handleBtnClick }>更改</button>
</div>
</Fragment>
);
}
handleBtnClick(){
this.setState({
isShow:!this.state.isShow
})
}
}
class Text extends Component {
componentWillUnmount(){
console.log("componentWillUnmount函數(shù)已執(zhí)行,組件從頁(yè)面中移除之前調(diào)用,Text組件移除");
}
render(){
console.log("Text組件被渲染");
return (
<h1>itclanCoder</h1>
);
}
}
const container = document.getElementById('root');
ReactDOM.render(<LifeCycle />, container);
效果如下所示:
大家可以自行將這些生命周期函數(shù)放到組件內(nèi)部當(dāng)中,進(jìn)行測(cè)試的,看每個(gè)生命周期執(zhí)行的順序就一目了然了的
說(shuō)完了組件的裝載,那么接下來(lái)就是組件的更新了
組件的更新
當(dāng)props或者state發(fā)生改變的時(shí)候,就會(huì)引起render函數(shù)的渲染,也就是會(huì)引發(fā)組件的更新,它與組件的裝載一樣,會(huì)觸發(fā)一些生命周期函數(shù)
更新組件時(shí):生命周期函數(shù)執(zhí)行的順序
- componentWillReceiveProps(nextProps,nextState):只要父組件的render函數(shù)被調(diào)用,在render函數(shù)里面被渲染的子組件就會(huì)經(jīng)歷更新的過(guò)程,無(wú)論父組件傳給子組件的props有沒(méi)有改變,都會(huì)觸發(fā)子組件的componentWillReceiveProps函數(shù)
你可以理解為,第一次渲染時(shí),父組件的componentWillReceiveProps函數(shù)不會(huì)被執(zhí)行,如果是第二次渲染時(shí),已經(jīng)存在于父組件中,則該componentWillReceiveProps才會(huì)執(zhí)行
注意:在掛載過(guò)程中,React不會(huì)針對(duì)初始props調(diào)用此方法,通過(guò)觸發(fā)setState方法更新過(guò)程不會(huì)調(diào)用這個(gè)函數(shù),這是因?yàn)檫@個(gè)函數(shù)適合根據(jù)新的props值(也就是nextProps)來(lái)計(jì)算出是不是要更新內(nèi)部狀態(tài)state
應(yīng)用場(chǎng)景:當(dāng)你希望只有在接收新的props時(shí)做一些邏輯時(shí),props改變需要相應(yīng)改變內(nèi)部state狀態(tài)時(shí),則使用componentWillReceiveProps蹬敲,比如:根據(jù)父組件傳入的數(shù)據(jù)初始化或重置組件內(nèi)部的某些state狀態(tài)
- shouldComponentUpdate:它決定一個(gè)組件什么時(shí)候不需要被渲染,在組件更新過(guò)程中,Render函數(shù)之前調(diào)用執(zhí)行,它同Render函數(shù)一樣,要求有返回結(jié)果的函數(shù)
返回一個(gè)boolean值,告訴React庫(kù)這個(gè)組件在這次更新過(guò)程是否要繼續(xù),如果該函數(shù)返回true,那么繼續(xù)更新,調(diào)用render函數(shù),反之,若函數(shù)返回false,那么立刻停止更新過(guò)程,便不會(huì)執(zhí)行render函數(shù)了的
這個(gè)函數(shù)是提高React的性能的,如果發(fā)現(xiàn)沒(méi)必要的渲染,那就干脆不用渲染了的,這個(gè)shouldComponentUpdate就可以做到
注意: forceUpdate不會(huì)觸發(fā)該函數(shù),也可以使用PureComponent替代該函數(shù),該函數(shù)做了內(nèi)部的優(yōu)化
// nextProps表示的是接下來(lái)我的props值會(huì)樣,nextState表示的是我的state會(huì)變成什么樣
shouldComponentUpdate(nextProps, nextState)
if(nextProps.props屬性 !== this.props.props屬性 || nextState.state屬性 !== this.state.state屬性)
return true;
}else{
return false
}
-
componentWillUpdate: 組件即將更新時(shí)調(diào)用,在Render函數(shù)之前調(diào)用
注意: 不要在該函數(shù)中通過(guò)this.setState再次改變state,如果需要,則在componentWillReceiveProps函數(shù)中改變 -
render:決定該組件UI渲染結(jié)果,返回的結(jié)果用于構(gòu)造DOM對(duì)象
注意:不能在render函數(shù)中調(diào)用setState湘换,如果在shouldComponentUpdate返回false,則render函數(shù)不會(huì)被調(diào)用 -
componentDidUpdate:組件更新完之后執(zhí)行骇径,有兩個(gè)參數(shù)prevProps和prevState假残,無(wú)論是父組件props的修改還是狀態(tài)的更改都會(huì)觸發(fā)該方法
應(yīng)用場(chǎng)景:如果希望無(wú)論props更改還是組件內(nèi)的狀態(tài)更改都能觸發(fā)一些邏輯,則可以使用componentDidUpdate,進(jìn)行業(yè)務(wù)處理,發(fā)送網(wǎng)絡(luò)請(qǐng)求
注意:在處理業(yè)務(wù)或發(fā)送網(wǎng)絡(luò)請(qǐng)求時(shí),一定要做好條件比較,否則容易造成死循環(huán)
組件的卸載
React組件從頁(yè)面中移除時(shí),在卸載的過(guò)程中,只涉及一個(gè)生命周期函數(shù)componentWillUnmount,由于該函數(shù)在組件刪除之前會(huì)被調(diào)用,所以該函數(shù)適合做一些清理性的工作
應(yīng)用場(chǎng)景: 清理無(wú)效的timer,取消未完成的網(wǎng)絡(luò)請(qǐng)求,清理已注冊(cè)的訂閱
注意:在這里使用setState時(shí)無(wú)效的
當(dāng)然對(duì)于React的生命周期,不同的版本,官方對(duì)它做了一些優(yōu)化和改動(dòng),這里介紹的是React Version 16.2.0版本的,生命周期過(guò)程圖如下所示
如果是最新的元旬,在React17.0版本中,生命周期函數(shù)如下所示
總結(jié)
本文主要講解了React的生命周期,只要理解了生命周期的圖譜,生命周期也就差不多了的,在constructor構(gòu)造器中初始化工作,componentWillMount在組件即將掛載之前執(zhí)行調(diào)用,常用于組件的啟動(dòng)工作,例如:Ajax數(shù)據(jù)的獲取,定時(shí)器的啟動(dòng)
當(dāng)然數(shù)據(jù)的請(qǐng)求最好放在componentDidMount函數(shù)中,而當(dāng)props或者state發(fā)生改變時(shí),會(huì)引起組件的渲染,也就是組件的更新,只要父組件的render函數(shù)被渲染了,就會(huì)觸發(fā)子組件的componentWillReceiveProps,而當(dāng)shouldComponentUpdate的函數(shù)返回true時(shí),則render函數(shù)會(huì)渲染,要是返回false時(shí),則render函數(shù)不會(huì)渲染
當(dāng)組件從頁(yè)面中移除時(shí),在卸載之前會(huì)觸發(fā)componentWillUnmount函數(shù),該函數(shù)常常用于組件銷毀時(shí)調(diào)用,清理無(wú)效的定時(shí)器timer,取消未完成的網(wǎng)絡(luò)(Ajax)請(qǐng)求,清理已注冊(cè)的訂閱