Overview
最近常有學習React相關的技術,寫了幾個React的小Demo,使用 React/Express
技術棧。實在太小,羞於拿出來細說。React 的確是一個值得追隨的技術。但React體系實在龐大,我目前僅略知一二。這裡要挑出來說的,是React的生命週期機制。Demo的學習過程中,對它的方便、易用之處實在是深有體會,在一些細節處也值得斟酌,在這裡做一下記錄,便於分享。
如果你接觸過React,大概對render
和componentWillMount
等,會相對的熟悉,因為它們再常用不過。但用歸用,其中的一些理論上的細節,往往容易在使用的過程中被忽略,使我們多敲了不少程式碼,心很累的 : )
通俗來講,React 將元件 component
在web中的形成、修改和渲染等劃分為若干個階段,組成元件的生命週期。在一個完整的生命週期內,一個元件會經過若干個階段,在特殊的階段元件會呼叫一個特別的lifecycle method
,即生命週期方法。如下:
constructor(props)
componentWillMount()
render()
componentDidMount()
- componentWillReceiveProps(nextProps)
- shouldComponentUpdate(nextProps, nextState)
- componentWillUpdate(nextProps, nextState)
- render( )* //理解上與3. render()略有不同,見下。
- componentDidUpdate(prevProps, prevState )
- componentWillUnmount( )
值得注意,這些生命週期是React 內建的,在特定條件下就會被呼叫。而開發者可以做的就是
override
(過載)這些方法,以實現想要的功能。
constructor
constructor(props)
,元件形成時呼叫。
constructor 函式可理解為元件的建構函式,從元件的類(class) 例項化一個元件例項。這個函式在元件形成時被呼叫,是所有生命週期函式中最先執行
的。在constructor函式內,如有必要,進行state的初始化以及繫結方法;否則可以省去constructor函式的宣告。
有以下幾點在開發時值得注意:
- constructor 函式內,在執行任何statement之前,必須是super() 函式,如果有引數須將引數帶上。這點跟Java很像。
- 在constructor 函式內,
this.props
返回undefined
- 不要在初試化state時引用props 裡的值,否則每當props更新時,都需要在componentWillReceiveProps 函式內對state進行更新。(同時這也涉及到元件state選取的原則,如有需要請閱讀Thinking in React)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class App extends Component { constructor(props) { super(props);//------------(1) console.log(this.props);// undefined ------------(2) //initialize the state this.state = { value: '', color: props.initialColor // 不可取 ------------(3) } //bind methods this.handleClick = this.handleClick.bind(this); } } |
componentWillMount
componentWillMount()
,在元件首次渲染(render
)之前呼叫。
mount
有安裝
之意,我們可以理解為元件首次被載入在web中
。因此每次頁面載入/重新整理,或者某個元件第一次載入進入web時可以呼叫componentWillMount( ) 函式。舉個例子,在首次進入文章列表時時,可在 componentWillMount 對所有文章進行查詢。這樣,在render之前,就能拿到所有文章的資料,以便在render中使用。
在componentWillMount ( ) 函式內,若對
this.state
進行更新,無法觸發重新渲染元件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class PostList extends Component { //... //在componentWillMount 元件內獲取所有部落格列表 componentWillMount(){ axios.get('/posts') .then(res=>{ //... }); } //在 render 函式內將拿到的部落格列表 渲染在頁面中 render(){ //... } } |
Render
render()
render 即 渲染函式,是編寫元件程式碼時,唯一一個必須
的函式。該函式須有返回值,返回一個元件,即最終渲染出來的元件。在使用元件的class進行元件例項化時,得到的便是其返回值。
返回值有兩種型別:
- 一個父標籤,這個父標籤內可以包含若干個子標籤,在最外層標籤必須只有一個。
false
或者null
,代表不渲染任何DOM
1 2 3 4 5 6 7 8 9 10 |
class App extends Component { //... render(){ return ( <div> //... </div> ) } } |
注意:
在render函式中只做與返回元件相關的工作,勿在其中對state
進行操作,可以保證每次呼叫render函式,返回的元件都是相同的。否則將加大專案維護成本。另外,如果
shouldComponentUpdate
函式返回false
,則不執行render函式。關於shouldComponentUpdate將在下面介紹。
componentDidMount
componentDidMount()
,一旦元件首次載入完成,便會呼叫
如果需要對渲染出來的DOM節點做任何操作,可以在此處進行。(提示
: this.refs 可獲取真實DOM)。
在該元件內設定state將會導致元件被重新渲染。
1 2 3 4 5 6 7 8 9 10 11 12 |
class App extends Component { //.. componentDidMount(){ //將會觸發元件重新渲染 this.setState({ value: '100' }): //對節點進行操作 this.refs.div.appendChild(newChild); } } |
上面對 React生命週期函式
中的constructor
/ componentWillMount
/ render
/ componentDidMount
四個函式進行了介紹。下面將繼續介紹另外5個方法。在此之前,先總結一下,下面列表中列出的3.render()
與8.render()
的在邏輯上的區別和聯絡。先上一個列表。
- constructor(props)
- componentWillMount( )
- render( )
- componentDidMount( )
componentWillReceiveProps(nextProps)
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate(nextProps, nextState)
render()
*componentDidUpdate(prevProps, prevState)
componentWillUnmount()
「兩個」render( )方法的區別
3.render( ) 與 8.render( )*
實質上,這兩個方法毫無區別。但這裡為什麼要提及它們之間的區別呢?其實,它們只是同一函式 render( ) 在元件生命週期的兩個不同階段的不同理解而已。
前一個 render( ) 方法指在元件第一次被載入進入頁面時,呼叫的 render( ) 方法;後一個則指除去第一次,之後呼叫的 render( ) 方法。
因此,我們更願意稱第一次的 render( ) 方法為 mount
( 安裝 ),稱後一個 render( ) 方法為 re-render
( 重新渲染 ) 。這也是為什麼元件首次 render 前後的方法名中帶有mount
一詞的緣故了。
這是 React 的伎倆,或者設計哲學吧。怎麼認為都行,我認為很有趣?
下面介紹的方法,都是圍繞第二個 render( ) ,即重新渲染 re-render 展開的。
componentWillReceiveProps
componentWillReceiveProps(nextprops)
,已載入的元件
在 props 發生變化時呼叫。
如果需要通過監聽 props 的改變來修改 state 的值,則可以通過過載該函式實現。
需要注意
,在有些情況下,元件的 props 未發生改變也會呼叫該函式。因此如果在該函式內的邏輯,只是想捕獲當前 props 與 接收的 nextProps 的不同來做出一些操作,則最好先將 props 與 nextProps 進行比較。
1.在
mounting
階段,即首次 render ,不呼叫 componentWillReceiveProps 方法。理解了兩個 render( ) 的不同,便知道這裡是為什麼了。2.
this.setState({…})
不觸發 componentWillReceiveProps 方法。因為該方法只監聽 this.props 的改變,不關心 this.state 值的變化。
1 2 3 4 5 6 7 8 |
class App extends Component { componentWillReceiveProps(nextProps){ //接收的顏色 與 當前顏色不同時 if (this.props.color !== nextProps.color){ ... } } } |
shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
返回
true
orfalse
要不要更新(重新渲染)元件?淺顯易懂。這個方法的返回值決定了,當 props 或者 state 值發生變化時,元件是否重新渲染。兩種情況:
- 返回
true
,重新渲染。緊接著,繼續執行componentWillUpdate()
→render()
→componentDidUpdate()
。 false
,不重新渲染。不再執行任何生命週期函式函式(亦不執行該元件的 render( ) 函式)。但是,並不妨礙其子元件。也就是說,如果其子元件的 props 或 state 發生改變時,只會取決於那個元件的 shouleComponentUpdate ( ) 方法的返回值。道理雖懂,但遇到是可能會犯迷糊,因為開發中常常會遇見元件巢狀的情況,父子元件之間傳遞同一套 props 或 state,一來二去,誰更新誰不更新,容易迷糊,需要仔細咯。
- 在絕大部分情況下,當 props 或 state 改變時,都是需要重新渲染元件的。
注意
,根據 React 官方 的說法,就算 shouldComponentUpdate( ) 方法返回false
,元件也會重新渲染。需要隨時注意官方文件的變化。
1 2 3 4 5 6 |
class PostList extends Component { shouldComponentUpdate(nextProps, nextState){ //return true;預設 return false;// 不更新元件 } } |
componentWillUpdate
componentWillUpdate(nextProps, nextState)
,當 shouldComponentUpdate( ) 方法返回 true 後呼叫。
這個方法提供了一個為重新渲染作準備的機會,意思是要在這裡,趁接下來的 render( ) 方法重新渲染之前,完成該完成的操作。這個方法在 mount 階段不會被呼叫,只在 re-render 階段被呼叫。
注意
,不要在該方法內呼叫this.setState({…})
,如有需要,請在 componentWillReceiveProps( ) 方法中完成。養成良好的程式設計規範。
1 2 3 4 5 6 7 8 9 10 |
class App extends Component { componentWillUpdate(nextProps, nextState){ var isLate = this.nextProps.isLate; if(isLate){ //... } else { //... } } } |
componentDidUpdate
componentDidUpdate(prevProps, preState)
,一旦元件首次更新(重新渲染)完成時呼叫。
因此像 componentDidMount( ) 一樣,如果需要對渲染出來的DOM節點做任何操作,可以在此處進行。(提示
: this.refs 可獲取真實DOM)。
在該元件內設定state將會導致元件被重新渲染。
1 2 3 4 5 6 7 8 9 10 11 12 |
class App extends Component { //.. componentDidUpdate(){ //將會觸發元件重新渲染 this.setState({ value: '100' }); //對節點進行操作 this.refs.div.appendChild(newChild); } } |
componentWillUnmount
componentWillUnmount()
,在元件即將被解除安裝(或銷燬)之前呼叫。
在這個方法中,適合做一些清理善後工作。例如清楚timer,取消網路請求,或者清除在 componentDidMount 或 componentDidUpdate 中生成的相關 DOM 節點。
總結
mount
與re-render
的是有區別的。mount
階段使用前一部分的四個方法( constructor / componentWillMount / render / componentDidMount),圍繞元件首次載入而呼叫;- 後一部分
re-render
相關的,使用 componentWillReceiveProps / shouldComponentUpdate / componentWillUpdate / render / componentDidUpdate ,圍繞元件重新渲染而呼叫。
我總結了一張流程圖和一個表格,以表示這些周期函式之間的關係,以及在何種情況下會呼叫這些函式。
注意:
componentWillUnmount 方法未包含其中。
mount |
props 變化 |
state 變化 |
---|---|---|
constructor | componentWillReceiveProps | shouldComponentUpdate |
componentWillMount | shouldComponentUpdate | (return true) ⏬ / 結束 |
render | (return true) ⏬ / 結束 | componentWillUpdate |
componentDidMount | componentWillUpdate | render |
/ | render | componentDidUpdate |
/ | componentDidUpdate | / |
完。
文章為本人原創,原文見本人個博:
淺析「React」生命週期(一)
淺析「React」生命週期(二)