了解React生命周期,對我們理解React的工作過程很有幫助。所有的事物都有自己的生命過程,React組件也不例外,同樣有著自己的生命周期,而它的生命周期可能會經歷如下的三個過程:
- 掛載過程
- 更新過程
- 銷毀過程
在經歷上面三個過程的時候,React庫會一次調用組件的一些成員函數,這些函數稱之為生命周期函數。因此,要定制一個react組件,實際上就是要定制這些生命周期函數。
掛載過程
在掛載過程中,當組件第一次被渲染時,會依次調用以下函數:
- constructor
- getInitialState
- getDefaultProps
- componentWillMount
- render
- componentDidMount
constructor
constructor也就是ES6語法中每個類的構造函數,而要創建一個組件類的實例,當然要調用對應的構造函數。但值得注意的是,不是每個React組件都需要定義自己的構造函數,使用構造函數一般都是以下兩個原因:
- 初始化state。因為組件中的任何函數都可能需要訪問state,因此在constructor中來初始化state是很好的選擇。
- 綁定成員函數的this。在es6中,累的每個成員函數在執行時的this并不是和類的實例所綁定的。而在鉤子凹函數中,this就是當前組件實例。所以,在此綁定this可以方便未來的調用。
getInitialState和getDefaultProps
已廢棄,不做討論
componentWillMount
在掛載過程中,componentWillMount會在render函數之前被調用。但通常來講我們不需要來定義componentWillMount,我們可以認為它所需要做的事情我們一般都在constructor中來做了。它所存在的意義更多的是為了與我們后面會提到的componentDidMount對稱。
render
render函數是React組件中最重要的函數。它并不是直接的去渲染動作,而是返回一個JSX描述的結構,然后最終由React來渲染過程。
對于render函數需要注意以下兩點:
- 首先是我們在定義React組件的時候,可以忽略其他所有組件都不實現,但一定要實現render,因為所有React組件的父類對除了render函數之外的生命周期函數都有默認實現。
- 其次render函數應該是一個純函數,應該完全根據this.state和this.props來決定返回的結果,而不應該產生任何副作用。因此,不應該在render中調用this.setState,這會引起狀態的變化。
componentDidMount
在render函數調用完之后componentDidMount函數并不會馬上被調用。它會在render函數返回的東西已經引發了渲染,組件已經被掛載到DOM書上時才會被調用。
前面提到的render函數知識返回JSX,并不負責直接的渲染,因此React庫需要把所有組件返回的結果全部集合起來,才可以知道如何產生對應的DOM修改。因此才會需要componentDidMount函數在render返回JSX,組件被渲染了才會調用來收尾。
它還有一個與componentWillMount函數不同的是,它只能在瀏覽器端被使用,而componentWillMount則可以使用在服務器端與瀏覽器端。這是由于只有瀏覽器端才會發生渲染的原因,服務器端可不會發生組件的掛載。
更新過程
當掛載過程結束之后,用戶們便可以看見我們的頁面了。但出于用戶體驗的角度來看,我們還需要提供更好的交互體驗,我們就需要組件可以隨著用戶操作來改變頁面內容,而props和state的修改,組件就會引發生更新過程。
更新過程包括以下生命周期函數:
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
componentWillReceiveProps
這是在我們調用this.setState()方法之后,子組件收到新的props之后會觸發的函數。通過它的名字其實我們就可已看出,它知識will,而不會去更改props的值。
江湖上有謠傳,這個函數只有在組件的props函數發生改變時才會調用這個函數。其實不然。實際上,只要父組件的render函數被調用,在render函數里面被渲染的子組件都會經歷更新過程,componentWillReceiveProps函數也就會觸發。
而且,通過this.setState方法出發的更新過程也不會觸發該函數。這是由于這個函數適合更具新得props值來計算出是不是要更新內部狀態的state。而this.setState就是用于更新組件的內部狀態的,這會引起死循環。
shouldComponentUpdate
shouldComponentUpdate是一個很特殊的函數,它決定了一個組件什么時候不需要渲染。它是除了render函數之外最重要的函數了。
他們兩個也是React生命周期函數中唯二要求返回結果的函數。其中render函數的返回結果用于構造DOM對象,而它會返回同一個布爾值,默認會返回ture,但假如這個函數返回false的話,更新流程就會被跳過,render也不會繼續被觸發。
說shouldComponentUpdate重要就是由于只要利用的好,我們可以在這個函數中自定義一些判斷來跳過不需要被更新的組件,從而提升性能。
componentWillUpdate和componentDidUpdate
componentWillUpdate和componentDidUpdate分別會在render的
前后被觸發。
componentWillUpdate無法調用this.setState()可以理解為更新流程到這一步想要再更改state已經晚了。如果有需要我們可以在之前的componentWillReceiveProps中更新state,React會把改變合并到一個更新流程里進行。
componentDidUpdate則是另一個比較適合我們發起ajax請求的地方,在這個方法里我們還可以比較前后的props變化,再決定是否發起網絡請求。一個比較實際的使用場景是保存用戶輸入到服務器,用戶可能會來來回回修改輸入的內容,但假如我們判斷在修改前后數據最終沒有改變,就沒有必要發起不必要的網絡請求了。我們也可以通過它來來調用其他的UI庫。而且他也可以在服務器端來調用,因為服務器端中使用React基本不會經歷更新過程,所有也就無所謂了。
卸載過程
在卸載流程中,名為componentWillUnmount的函數會被觸發。在組件掛載之后,我們可能定義了一些計時器、綁定了事件監聽函數等等,在卸載流程的生命周期函數中,也是我們解綁這些函數的合適位置。
它的工作也一般和componentDidMount有關。比如,在componentDidMount中用非React的方法創造一些DOM元素,如果撒手不管就可能造成內存泄漏,這就需要componentWillUnmount函數來吧這些創造的DOM元素清理掉。