React首次渲染過程


一、JSX如何生成element

這里是一段寫在render里的jsx代碼。

return (
    <div className="cn">
         <Header> Hello, This is React </Header>
         <div>Start to learn right now!</div>
         Right Reserve.
    </div>
)

首先,它會經過babel編譯成React.createElement的表達式。

return (
    React.createElement(
        'div',
        { className: 'cn' },
        React.createElement(
            Header,
            null,
            'Hello, This is React'
        ),
        React.createElement(
            'div',
            null,
            'Start to learn right now!'
        ),
        'Right Reserve'
    )
)

createElement從它的名字就可以看出,這是用來生成element的。element在React里,其實就是組成虛擬DOM 樹的節點,它用來描述你想要在瀏覽器上看到什么。
它的參數有三個:

  1. type -> 標簽
  2. attributes -> 標簽屬性,沒有的話,可以為null
  3. children -> 標簽的子節點

這個React.createElement的表達式會在render函數被調用的時候執行,換句話說,當render函數被調用的時候,會返回一個element。

element其實就是一個對象,如下:

{
  type: 'div',
    props: {
      className: 'cn',
        children: [
          {
            type: function Header,
            props: {
                children: 'Hello, This is React'
            }
          },
          {
            type: 'div',
            props: {
                children: 'start to learn right now!'
            }
          },
          'Right Reserve'
      ]
  }
}

我們來觀察一下這個對象的children,現在有三種類型:
1、string

2、原生DOM節點

3、React Component - 自定義組件

除了這三種,還有兩種類型:

4、fale ,null, undefined,number

5、數組 - 使用map方法的時候

這里需要記住一個點:element不一定是Object類型

二、element如何生成真實節點

順利得到element之后,我們再來看看React是如何把element轉化成真實DOM節點的。

首先,需要去初始化element,初始化的規則如下:

先判斷是否為Object類型,是的話,看它的type是否是原生DOM標簽,是的話,給它創建ReactDOMComponent的實例對象,其他同理。

這時候有的人可能會有所疑問:這些個ReactDOMComponent, ReactCompositeComponentWrapper怎么開發的時候都沒有見過?

其實這些都是React的私有類,React自己使用,不會暴露給用戶的。它們的常用方法有:mountComponent,updateComponent等。其中mountComponent 用於創建組件,而updateComponent用於用戶更新組件。而我們自定義組件的生命周期函數以及render函數都是在這些私有類的方法里被調用的。

既然這些私有類的方法那么重要我們就先來簡單了解一下吧~

ReactDOMComponent

首先是ReactMComponent的mountComponent方法,這個方法的作用是:將element轉成真實DOM節點,並且插入到相應的container里,然后返回markup(realDOM)。

由此可知ReactDOMComponent的mountComponent是element生成真實節點的關鍵。

下面看個栗子它是怎么做到的吧。

假設有這樣一個type類型是原生DOM的element:

{
  type: 'div',
    props: {
    className: 'cn',
      children: 'Hello world',
    }
}

簡單mountComponent的實現:

mountComponent(container) {
  const domElement = document.createElement(this._currentElement.type);
  const textNode = document.createTextNode(this._currentElement.props.children);

  domElement.appendChild(textNode);
  container.appendChild(domElement);
  return domElement;
}

其實實現的過程很簡單,就是根據type生成domElement,再將子節點append進來返回。當然,真實的mountComponent沒有那么簡單,感興趣的可以自己去看源碼啦。

這里需要記住的一個點是:這個類的mountComponent方法會自己操作瀏覽器DOM元素。

講完ReactDOMComponent,再來看看ReactCompositeComponentWrapper。

ReactCompositeComponentWrapper

這個類的mountComponent方法作用是:實例化自定義組件,最后是通過遞歸調用到ReactDOMComponent的mountComponent方法來得到真實DOM。

注意:也就是說他自己是不直接生成DOM節點的。

那這個遞歸是一個怎樣的過程呢?我們通過首次渲染來看下。

首次渲染
假設我們有一個Example的組件,它返回

hello world
這樣一個標簽。

首次渲染的過程如下:

首先從React.render開始,由於我們剛剛說,render函數被調用的時候會返回一個element,所以此時返回給我們的element是:

{
  type: function Example,
  props: {
    children: null
  }
}

由於這個type是一個自定義組件類,此時要初始化的類是ReactCompositeComponentWrapper,接着調用它的mountComponent方法。這里面會做四件事情,詳情可以看上圖。其中,第二步的render的得到的element為:

{
  type: 'div',
    props: {
    children: 'Hello World'
  }
}

由於這個type是一個原生DOM標簽,此時要初始化的類是ReactDOMComponent。接下來它的mountComponent方法就可以幫我們生成對應的DOM節點放在瀏覽器里啦。

這時候有人可能會有疑問,如果第二步render出來的element 類型也是自定義組件呢?

這時候它就會去調用ReactCompositeComponentWrapper的mountComponent方法,從而形成了一個遞歸。不管你的自定義組件嵌套多少層,最后總會生成原生dom類型的element,所以最后一定能調用到ReactDOMComponent的mountComponent方法。

感興趣的可以自己在打斷點看下這個遞歸的過程。

由我打的斷點圖可以看出在ReactCompositeComponent的mountComponent被調用多次之后,最后調用到了ReactDOMComponent的mountComponent方法。

還有一個問題:前面我們說自定義組件的生命周期跟render函數都是在私有類的方法里被調用的,現在只看到render函數被調用了,那么首次渲染時候生命周期函數 componentWillMount 跟 componentDidMount 在哪被調用呢?

由圖可知,在第一步得到instance對象之后,就會去看instance.componentWillMount是否有被定義,有的話調用,而在整個渲染過程結束之后調用componentDidMount。

以上,就是渲染原理的部分,讓我們來總結以下:

JSX代碼經過babel編譯之后變成React.createElement的表達式,這個表達式在render函數被調用的時候執行生成一個element。

在首次渲染的時候,先去按照規則初始化element,接着ReactComponentComponentWrapper通過遞歸,最終調用ReactDOMComponent的mountComponent方法來幫助生成真實DOM節點。

參考文檔:https://zhuanlan.zhihu.com/p/43145754


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM