文:徐超,《React進階之路》做者受權發佈,轉載請註明做者及出處前端
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更新組件狀態?哪些方法中不能夠呢?
能夠的方法
componentWillMount、componentDidMount、componentWillReceiveProps、componentDidUpdate
這裏有幾個注意點:
不能夠的方法
其餘生命週期方法都不能調用setState,主要緣由有兩個:
先看下面的一個例子:
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方法時也有如下說明:
CallingsetState()
in this method will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though therender()
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