React學(xué)習(xí)(8)-React中組件的生命周期

前言

為了進(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è)生命周期圖譜的


完整生命周期圖.png
  • 組件的裝載(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);

效果如下所示:


組件的裝載過(guò)程.gif

大家可以自行將這些生命周期函數(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ò)程圖如下所示


React Version16.2.png

如果是最新的元旬,在React17.0版本中,生命周期函數(shù)如下所示


React17.0生命周期過(guò)程圖.png

總結(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è)的訂閱

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末守问,一起剝皮案震驚了整個(gè)濱河市匀归,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耗帕,老刑警劉巖穆端,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異仿便,居然都是意外死亡体啰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門嗽仪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)荒勇,“玉大人,你說(shuō)我怎么就攤上這事闻坚」料瑁” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)仅偎。 經(jīng)常有香客問(wèn)我跨蟹,道長(zhǎng),這世上最難降的妖魔是什么橘沥? 我笑而不...
    開(kāi)封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任窗轩,我火速辦了婚禮,結(jié)果婚禮上座咆,老公的妹妹穿的比我還像新娘痢艺。我一直安慰自己,他們只是感情好介陶,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布堤舒。 她就那樣靜靜地躺著,像睡著了一般斤蔓。 火紅的嫁衣襯著肌膚如雪植酥。 梳的紋絲不亂的頭發(fā)上镀岛,一...
    開(kāi)封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天弦牡,我揣著相機(jī)與錄音,去河邊找鬼漂羊。 笑死驾锰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的走越。 我是一名探鬼主播椭豫,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼旨指!你這毒婦竟也來(lái)了赏酥?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谆构,失蹤者是張志新(化名)和其女友劉穎裸扶,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體搬素,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呵晨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了熬尺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摸屠。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖粱哼,靈堂內(nèi)的尸體忽然破棺而出季二,到底是詐尸還是另有隱情,我是刑警寧澤揭措,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布戒傻,位于F島的核電站税手,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏需纳。R本人自食惡果不足惜芦倒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望不翩。 院中可真熱鬧兵扬,春花似錦、人聲如沸口蝠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妙蔗。三九已至傲霸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間眉反,已是汗流浹背昙啄。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寸五,地道東北人梳凛。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梳杏,于是被迫代替她去往敵國(guó)和親韧拒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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