React 深刻系列4:組件的生命週期

文:徐超,《React進階之路》做者

受權發佈,轉載請註明做者及出處前端


React 深刻系列4:組件的生命週期

React 深刻系列,深刻講解了React中的重點概念、特性和模式等,旨在幫助你們加深對React的理解,以及在項目中更加靈活地使用React。

組件是構建React應用的基本單位,組件須要具有數據獲取、業務邏輯處理、以及UI呈現的能力,而這些能力是要依賴於組件不一樣的生命週期方法的。組件的生命週期分爲3個階段:掛載階段、更新階段、卸載階段,每一個階段都包含相應的生命週期方法。由於是深刻系列文章,本文不會仔細介紹每一個生命週期方法的使用,而是會重點講解在使用組件生命週期時,常常遇到的疑問和錯誤使用方式。瀏覽器

服務器數據請求

初學者在使用React時,經常不知道什麼時候向服務器發送請求,獲取組件所需數據。對於組件所需的初始數據,最合適的地方,是在componentDidMount方法中,進行數據請求,這個時候,組件完成掛載,其表明的DOM已經掛載到頁面的DOM樹上,即便獲取到的數據須要直接操做DOM節點,這個時候也是絕對安全的。有些人還習慣在constructor或者componentWillMount中,進行數據請求,認爲這樣能夠更快的獲取到數據,但它們相比componentDidMount的執行時間,提早的時間實在是太微乎其微了。另外,當進行服務器渲染時(SSR),componentWillMount是會被調用兩次的,一次在服務器端,一次在客戶端,這時候就會致使額外的請求發生。安全

組件進行數據請求的另外一種場景:由父組件的更新致使組件的props發生變化,若是組件的數據請求依賴props,組件就須要從新進行數據請求。例如,新聞詳情組件NewsDetail,在獲取新聞詳情數據時,須要傳遞新聞的id做爲參數給服務器端,當NewsDetail已經處於掛載狀態時,若是點擊其餘新聞,NewsDetail的componentDidMount並不會從新調用,於是componentDidMount中進行新聞詳情數據請求的方法也不會再次執行。這時候,應該在componentWillReceiveProps中,進行數據請求:服務器

componentWillReceiveProps(nextProps) {
  if(this.props.newId !== nextProps.newsId) {
    fetchNewsDetailById(nextProps.newsId)  // 根據最新的新聞id,請求新聞詳情數據
  }
}

若是進行數據請求的時機是由頁面上的交互行爲觸發的,例如,點擊查詢按鈕後,查詢數據,這時只須要在查詢按鈕的事件監聽函數中,執行數據請求便可,這種狀況通常是不會有疑問的。前端工程師

更新階段方法的調用

組件的更新是組件生命週期中最複雜的階段,也是涉及到最多生命週期方法的階段。app

正常狀況下,當組件發生更新時,組件的生命週期方法的調用順序以下:異步

componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

// 組件收到新的props(props中的數據並不必定真正發生變化)-> 決定是否須要繼續執行更新過程 -> 組件表明的虛擬DOM即將更新 -> 組件從新計算出新的虛擬DOM -> 虛擬DOM對應的真實DOM更新到真實DOM樹中

父組件發生更新或組件自身調用setState,都會致使組件進行更新操做。父組件發生更新致使的組件更新,生命週期方法的調用狀況同上所述。若是是組件自身調用setState,致使的組件更新,其生命週期方法的調用狀況以下:函數

shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

可見,這種狀況下componentWillReceiveProps並不會被調用。fetch

當組件的shouldComponentUpdate返回false時,組件會中止更新過程,這時候生命週期方法的調用順序以下:this

componentWillReceiveProps -> shouldComponentUpdate -> 結束

或(組件自身調用setState,致使的組件更新):

shouldComponentUpdate -> 結束

setState的時機

組件的生命週期方法衆多,哪些方法中能夠調用setState更新組件狀態?哪些方法中不能夠呢?

  • 能夠的方法

    componentWillMount、componentDidMount、componentWillReceiveProps、componentDidUpdate

    這裏有幾個注意點:

    1. componentWillMount 中同步調用setState不會致使組件進行額外的渲染,組件經歷的生命週期方法依次是componentWillMount -> render -> componentDidMount,組件並不會由於componentWillMount中的setState調用再次進行更新操做。若是是異步調用setState,組件是會進行額外的更新操做。不過實際場景中不多在componentWillMount中調用setState,通常能夠經過直接在constructor中定義state的方式代替。
    2. 通常狀況下,當調用setState後,組件會執行一次更新過程,componentWillReceiveProps等更新階段的方法會再次被調用,但若是在componentWillReceiveProps中調用setState,並不會額外致使一次新的更新過程,也就是說,當前的更新過程結束後,componentWillReceiveProps等更新階段的方法不會再被調用一次。(注意,這裏仍然指同步調用setState,若是是異步調用,則會致使組件再次進行渲染)
    3. componentDidUpdate中調用setState要格外當心,在setState前必須有條件判斷,只有知足了相應條件,才setState,否組組件會不斷執行更新過程,進入死循環。由於setState會致使新一次的組件更新,組件更新完成後,componentDidUpdate被調用,又繼續setState,死循環就產生了。
  • 不能夠的方法

    其餘生命週期方法都不能調用setState,主要緣由有兩個:

    1. 產生死循環。例如,shouldComponentUpdate、componentWillUpdate 和 render 中調用setState,組件本次的更新尚未執行完成,又會進入新一輪的更新,致使不斷循環更新,進入死循環。
    2. 無心義。componentWillUnmount 調用時,組件即將被卸載,setState是爲了更新組件,在一個即將卸載的組件上更新state顯然是無心義的。實際上,在componentWillUnmount中調用setState也是會拋出異常的。

render次數 != 瀏覽器界面更新次數

先看下面的一個例子:

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      bgColor: "red"
    }
  }

  render() {
    var {bgColor} = this.state
    return (
      <div style = {{backgroundColor: bgColor}}> 
        Test
      </div>
    );
  }
  
  componentDidMount() {
    this.setState({
      bgColor: "yellow"
    })
  }
}

當咱們觀察瀏覽器渲染出的頁面時,頁面中Test所在div的背景色,是先顯示紅色,再變成黃色呢?仍是直接就顯示爲黃色呢?

答案是:直接就顯示爲黃色!

這個過程當中,組件的生命週期方法被調用的順序以下:

constructor -> componentWillMount -> render -> componentDidMount -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

組件在掛載完成後,由於setState的調用,將當即執行一次更新過程。雖然render方法被調用了兩次,但這並不會致使瀏覽器界面更新兩次,實際上,兩次DOM的修改會合併成一次瀏覽器界面的更新。React官網介紹componentDidMount方法時也有如下說明:

Calling setState() in this method will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state.

這說明,組件render的次數 不必定等於 瀏覽器界面更新次數。雖然JS的執行和DOM的渲染分別由瀏覽器不一樣的線程完成,但JS的執行會阻塞DOM的渲染,而上面的兩次render是在一個JS事件週期內執行的,因此在兩次render結束前,瀏覽器不會更新界面。

下篇預告:

React 深刻系列5:事件處理


新書推薦《React進階之路》

做者:徐超

畢業於浙江大學,碩士,資深前端工程師,長期就任於能源物聯網公司遠景智能。8年軟件開發經驗,熟悉大前端技術,擁有豐富的Web前端和移動端開發經驗,尤爲對React技術棧和移動Hybrid開發技術有深刻的理解和實踐經驗。



美團點評廣告平臺大前端團隊招收20192020年前端實習生(偏動效方向)

有意者郵件:yao.zhou@meituan.com

相關文章
相關標籤/搜索