React元件生命週期——精華筆記

小小石馬農發表於2017-10-07

在學習使用React開發時,元件的生命週期是個繞不開的話題,也是很容易搞混的,下面是我在仔細查閱資料後,嘔心瀝血歸結的精華,與大家分享,希望對大家有幫助。

什麼是生命週期

元件的生命週期,本質上就是狀態機,確定了輸入,輸出也就確定了。本質上就是狀態和轉移的結合,不同的狀態對應不同的輸出,狀態與狀態之間,有時候會發生狀態的改變或變更,會觸發許多不同的鉤子函式,從而讓開發者有機會做出響應,因為狀態轉化是由react進行維護的,開發者無法直接參與到狀態轉化中,但是react給我們提供了許多鉤子函式,我們可以通過編寫這些鉤子函式,這樣react在進行事件轉化的時候可以呼叫這些鉤子函式。

生命週期的組成部分

初始化階段 ——> 執行中階段 ——> 銷燬階段

初始化階段:

用元件程式碼生成元件例項,這裡的元件程式碼也就是編寫的class,其實類似與物件導向中的class一樣,我們使用的不是class物件,而是class初始化出來的例項物件,例如,我們有個HelloWorld元件,我們在頁面的兩個地方使用了HelloWorld元件,這兩個地方渲染出來的是兩個例項,例項和例項之間是不同的,但是他們來自於同一個元件,也就是說他們是用同一個元件初始化出來的,在初始化階段,元件會進行一些初始化狀態的設定,以及渲染也就是render,在初始化完成之後,例項就會真正顯示在頁面上,可以被使用者使用。

執行中狀態:

對於例項物件來說,絕大部分時間都處在執行中狀態,執行中例項的狀態可能發生改變,從而觸發一系列的鉤子函式,最終這些改變會導致頁面可能重新渲染,這裡可能是因為一些改變也可能不會重新渲染,只是內部的狀態發生了變動,例如,一個輸入框,向其中輸入文字時,可能只會觸發一些資料上的操作,並不會讓頁面重新渲染。

銷燬階段:

銷燬階段比較容易理解,當我們不使用元件的時候,元件的例項就會被銷燬。舉個例子,一個父元件包含一個子元件,當父元件渲染的時候,子元件就會經歷初始化階段和執行中階段,假如說父元件在某一時刻刪除了子元件,也就是render的結果中不包含子元件,那麼之前渲染好的子元件就不需要了,因此它就會被react銷燬。

說明:

元件的生命週期就是有初始化,執行中,和銷燬階段構成,這裡需要引起注意的是,這三者發生的時間是不固定的,需要根絕頁面來進行判斷,不過我們不關心這三者發生的具體時間,這三個階段只是在邏輯上對元件的生命週期進行了分類,在我們實際編寫元件的時候,我們主要關注的是它們的鉤子函式以及它們的用法。

元件中的props(屬性)和state(狀態)

屬性(props)是由父元件傳遞給子元件的;狀態(state)是子元件內部維護的一些資料,狀態發生變化時,元件也會實時更新一個state對應一個render的結果這樣我們就能知道在不同的state下元件會render出怎樣的結果,從而就能知道元件在頁面上展示的內容是什麼

不同生命週期內可以自定義的函式

初始化階段:

getDefaultProps: 獲取例項的預設屬性,這個函式只會在元件的第一個例項被初始化的時候被呼叫,因為同一個元件的例項拿到的預設屬性是一樣的,只呼叫一次,例項之間共享引用。
共享引用:在js中用兩種型別的資料,一種是值型別的資料,比如說字,字串,布林值,另一種型別的資料是引用型別的資料,有物件,陣列,函式,當函式返回的是引用型別的資料,那麼React會把引用儲存起來,在建立不同的例項時,它會使用同一個引用當作屬性,而我們都知道引用指向的都是同一個地址,所以說不同例項之間操作的是同一個資料,所以在使用的時候應該注意返回的是引用還是值。

getInitialState:獲取例項的初始化狀態,初始化每個例項特有的狀態。
每個例項:從這個函式開始,每個例項被初始化的時候,都會呼叫它,不像getDefaultProps只呼叫一次。
初始化狀態:第一個函式處理的屬性,這個函式處理的是狀態,由於狀態是每個例項的內部資訊,每個例項要自己維護自己的狀態,所以說不同的例項有不同的狀態,那麼他們都需要呼叫getInitialState方法

componentWillMount:元件即將被裝載,render之前最後一次修改狀態的機會,元件即將被渲染到頁面上,在這一階段元件還沒有被真正的渲染,在這個時候,還是可以修改狀態的,但在render中就不應該再修改狀態了。

render:元件在render中生成虛擬的DOM結點,也就是jsx,最後由React將虛擬的DOM結點渲染成真實的DOM結點,並放置在頁面中,讓使用者可以看到,並且可以進行互動。
注意事項:

  1. 只能訪問this.props和this.state,這是它特有的兩個資料來源,除此之外,不應該獲取元件包含的其他資訊;
  2. 只有一個頂層元件,也就是render返回值只能是一個元件,當然這個元件可以包含很多子元件,也可以包含很多子程式碼,但是本質上是一個元件,不能返回一個陣列;
  3. 不允許修改狀態和DOM輸出,這是其實修改操作是可以實現的,只是React非常不推薦這樣的操作,因為在這裡修改了狀態或者DOM輸出的話,那麼render就無法在服務端使用,雖然render函式大部分都是在客戶端使用,如果需要提高網站的載入效能,可以將render在服務端進行處理,這是如果render需要修改狀態和dom輸出,在服務端是沒有這種環執行境的,所以說如果修改了狀態或dom輸出,那麼render只能使用在瀏覽器中,這樣會限制系統的效能,此外在render中修改了狀態或dom輸出的話,會使得程式碼的邏輯變得非常的複雜,很難再通過狀態分析出結果,React設計的目的之一就是是程式碼的邏輯變得清晰簡單,這種修改操作其實違背了react的初衷的,無論是自己還是別人,都很難再看懂這段程式碼,所以一定不要在render中修改狀態和DOM輸出。

componentDidMount:成功render並渲染完成真實DOM之後觸發,可以修改DOM,元件裝載之後,在這個函式被呼叫的時候,元件已經被渲染到了頁面上,使用者可以和頁面進行互動。這個函式中,我們可以修改DOM,可以進行一些自定義的操作,有可能是工具本身還不相容React,也有可能是我們要進行一些特殊的操作,無論如何,操作真正的DOM可以也必須在這個函式中完成,實際上,想在前面完成也是不可能的,因為只有在render結束之後,真正的DOM才被真正渲染到了頁面上,我們才可以操作它。

執行中階段:

componentWillReceiveProps:父元件修改屬性觸發,可以修改新屬性,修改狀態,在元件將要接受到屬性的時候呼叫,當元件的屬性發生了變化,比如說父元件改變了元件的屬性,那麼元件就“可能”需要更新,因為這個函式是在元件接受到屬性之前觸發的,屬性在傳送到元件之前,開發者有機會對屬性進行處理,比如修改屬性或更新內部狀態,在這時,屬性還沒有被傳送給元件。

shouldComponentUpdate:從名字還看,這個函式是個疑問句,其意思是React詢問開發者詢問開發者元件是否需要更新,返回false會阻止render呼叫;但元件接受到新屬性,新狀態時觸發這個函式,讓開發者來決定是否更新,有的時候狀態發生變化元件可能不需要更新,只是更新一些資料,不需要更新顯示出來的內容,這裡可以選擇返回false,後面的三個函式都是與更新相關的,如果這裡返回的是false的話,那麼就會直接中斷流程,後面的幾個函式都不會執行,這裡需要說明下,在大部分時間都不需要使用這個函式,只有在真正找到了專案的瓶頸之後,再根據實際需要,小心的使用這個函式,因為對這個函式使用不當的話,會導致很多無法預料的問題。

componentWillUpdate:和componentWillMount函式類似,它會在render函式觸發之前呼叫,但是不能修改屬性和狀態,因為這裡是處於執行中階段,所以是進行更新操作,而不是裝載操作。

render:和初始化階段的render函式一樣,只能訪問this.props和this.state,只有一個頂層元件,不允許修改狀態和DOM輸出。

componentDidUpdate:這個函式和componentDidMount函式類似,會在render結束之後,新的真實DOm渲染完之後呼叫,可以修改DOM。

銷燬階段:

componentWillUnmount: 在刪除元件之前操作,比如計時器和事件監聽器;
因為這些是開發者手動加上去的,react並不知情,所以說必須進行手動清理,react是不會自動處理他們的這個函式會在銷燬操作真正執行之前呼叫,給開發者最後的機會,來進行一些清理操作。

總結

生命週期在開發者初期不容易理解也體會不到其重要性,但在深入專案開發階段是非常重要的,要完全理解各個階段的元件生命週期是一個需要經驗和知識積累的過程,慢慢體會去吧~

參考資料

React小書:huziketang.com/books/react…
極客學院React教程:www.jikexueyuan.com/course/987.…

相關文章