【Under-the-hood-ReactJS-Part14】React源碼解讀

接上文,git

React流程圖:
https://bogdan-lyashenko.gith...github

最後的最後

更新方法基於子元素上的多個屬性去處理子元素。這裏會有幾種場景,可是技術上來講主要是兩種。一種是子元素仍然是‘複雜’對象,也就是說子元素仍是React組件,React須要遞歸處理嵌套的子組件直到到達他們的內容層級。還有一種,就是子元素已是內容層級裏,內容就是字符串或者數字或者其餘簡單類型。dom

處理方式是根據nextProps.children的類型來判斷的。在咱們的列子中,組件
<ExampleApplication>組件有三個子元素,button,childCmp 和 text string。svg

咱們看下它是如何運做的。
在Examplication子元素的第一次迭代期間,子元素的類型不是內容,因此須要進入到‘複雜’組件的處理邏輯。咱們遍歷全部的子元素,按以前處理它們父元素的過了處理它們。順便提下,驗證shouldUpdateReactComponent的代碼塊會使人有點疑惑,表面上看起來這個是用來檢測是否須要更新,可是實際上,它除了檢測更新,還檢測刪除,新建操做(爲了簡化流程圖,相應的代碼沒有展現在流程圖中)。以後,咱們將舊元素和當前元素,若是一些子元素被移除了,則咱們須要卸載對應的組件並同時移除它。性能

接下去,在第二次迭代過程當中,咱們須要處理button,這個相對來講比較簡單,由於button的子元素就是文本,內容就是‘set state button'。咱們檢測下以前的內容是否跟如今的保持一致,嗯,文本沒有發生改變,因此咱們不須要更新button。邏輯上來講這樣很正確,其實這就是虛擬DOM的做用,如今虛擬DOM聽起來就具體了些,是否是?React會維護內部DOM,同時只在須要時纔去處理真正的DOM節點,經過這種方式,很天然的會提升性能。到這裏,你應該已經能理解React的設計思想了,這以後,咱們對ChildCmp進行更新,它的子元素會被遍歷直到它的內容層級並進行更新。在咱們的列子裏,經過click和setState的調用,'click state message'會對this.props.message進行更新。this

//...
onClickHandler() {
    this.setState({ message: 'click state message' });
}

render() {
    return <div>
        <button onClick={this.onClickHandler.bind(this)}>set state button</button>
        <ChildCmp childMessage={this.state.message} />
//...

如今咱們要對元素內容進行更新,事實上,是替換它的內容。那麼是如何進行更新的呢?一個相似擁有配置信息的配置對象會被解析,而且配置對象裏的定義的動做會被執行。對應咱們的例子,文本更新的配置會像以下:設計

{
  afterNode: null,
  content: "click state message",
  fromIndex: null,
  fromNode: null,
  toIndex: null,
  type: "TEXT_CONTENT"
}

正如你所見,它幾乎就是一個空對象,這個文本內容更新例子是至關直白的。配置對象裏有不少字段,這是由於在對DOM節點進行移動時,配置對象會比文本更新的配置對象相對來講會更復雜些:code

看下源碼,這應該會讓咱們有個更清晰的認識:component

//src\renderers\dom\client\utils\DOMChildrenOperations.js#172
processUpdates: function(parentNode, updates) {
    for (var k = 0; k < updates.length; k++) {
      var update = updates[k];

      switch (update.type) {
        case 'INSERT_MARKUP':
          insertLazyTreeChildAt(
            parentNode,
            update.content,
            getNodeAfter(parentNode, update.afterNode)
          );
          break;
        case 'MOVE_EXISTING':
          moveChild(
            parentNode,
            update.fromNode,
            getNodeAfter(parentNode, update.afterNode)
          );
          break;
        case 'SET_MARKUP':
          setInnerHTML(
            parentNode,
            update.content
          );
          break;
        case 'TEXT_CONTENT':
          setTextContent(
            parentNode,
            update.content
          );
          break;
        case 'REMOVE_NODE':
          removeChild(parentNode, update.fromNode);
          break;
      }
    }
  }

咱們的實例會走到'TEXT_CONTENT'的分支裏,而後這就是最後一步了,React調用setTextContent方法,此方法會對真正的DOM節點進行內容更改。對象

不錯不錯,最終內部被更新到了頁面上,這就是一個重繪的過程了。還有什麼東西沒有講到嗎?嗯,不要着急,讓咱們先完成這個更新過程。全部的東西都已經準備完畢,全部咱們組件的componentDidUpdate方法會被調用。那麼,這種延遲調用是如何實現的呢?沒錯,就是使用事務包裝器。就像以前提到的,‘髒’組件的更新是被ReactUpdateFlushtransaction給包裝過的,其中一個包裝器裏就有調用this.callbackQueue.notifyAll的邏輯,也就是在這裏,componentDidUpdate會被調用。完美!

如今,咱們真的完成了整個過程。

相關文章
相關標籤/搜索