1. 程式人生 > >React學習 -- React原始碼(2)Virtual DOM模型

React學習 -- React原始碼(2)Virtual DOM模型

VirtualDOM模型負責VirtualDOM底層框架的建立工作,它擁有整套的VirtualDOM標籤,並負責虛擬節點的建立、更新、刪除等工作。

下面是一個簡易的VirtualDOM模型,它並不複雜,只需要具備:

標籤名
節點屬性:樣式,屬性,事件等
子節點
標識ID

{
    tagName: 'div',
    properties: {
        style: {} 
    },
    children: [],  
    key: 1
}

virtual DOM中的節點稱為ReactNode,分為:ReactElement、ReactFragment、ReactText。其中ReactElement又分為ReactComponentElement和ReactDOMElement。

通過方法createElement建立React虛擬元素

通過JSX建立的虛擬元素最終都會被編譯成呼叫React的createElement方法。

在createElement方法中,對引數做了簡單的引數修正(例如,提取config內容,處理children等),然後根據修正過的引數,返回了一個ReactElement物件。

初始化元件入口

當使用React建立元件時,首先會呼叫instantiateReactComponent,則是初始化元件的入口函式。該函式會根據node型別,區分不同元件的入口。

當node為空時,說明node不存在,則初始化空元件:ReactEmptyComponent.create(instan- tiateReactComponent)
當node型別為物件時,即是DOM標籤或自定義元件。初始化DOM標籤元件使用方法:ReactNativeComponent.createInternalComponent (element),初始化自定義元件使用方法:ReactCompositeComponentWrapper()。
當node型別為字串或者數字,則初始化文字元件:ReactNativeComponent.create InstanceForText(node)
如果是其他情況,則不做任何處理。

以下是instantiateReactComponent方法原始碼:

function instantiateReactComponent(node, parentCompositeType) {
    var instance;
    //空元件(ReactEmptyComponent)
    if (node === null || node === false) {
        instance = ReactEmptyComponent.create(instantiateReactComponent); 
    }
    if (typeof node === 'object'
) { var element = node; if (typeof element.type === 'string') { // DOM標籤(ReactDOMComponent) instance = ReactNativeComponent.createInternalComponent(element); } else if (isInternalComponentType(element.type)) { // 不是字串表示的自定義元件暫時無法使用 instance = new element.type(element); } else { // 自定義元件(ReactCompositeComponent) instance = new ReactCompositeComponentWrapper(); } } else if (typeof node === 'string' || typeof node === 'number') { // 字串或數字(ReactTextComponent) instance = ReactNativeComponent.createInstanceForText(node); } else { // 不做處理 } // 設定例項 instance.construct(node); // 初始化引數 instance._mountIndex = 0; instance._mountImage = null; return instance; }

這裡寫圖片描述

關於元件是什麼的問題:
這裡的元件就是我們在使用react程式設計時,採用class方法或者直接用函式方法宣告的React元件,所有這些元件都最終會被解釋為VirtualDOM元素。

文字元件 ##

當node節點為文字節點是,是不算VirtualDOM元素的,但是React為了保持渲染的一致性,將其封裝為文字元件:ReactDOMTextComponent。

在執行mountComponent方法時,ReactDOMTextComponent通過transaction.useCreateElement判斷該文字是否是通過createElement方法建立的節點,如果是,則為該節點建立相應的標籤和識別符號。如果不是,React將直接返回文字內容。

DOM標籤元件

當開發者在React中使用DOM標籤時,例如div,此時的div並不是原生div,其實是React生成的VirtualDOM物件。React的大部分工作都是在VirtualDOM中完成的,這些工作並不會直接操作和汙染原生的DOM。這樣在效能方面能大有改進,並且降低了直接操作DOM而導致錯誤的風險。

ReactDOMComponent 對於 Virtual DOM 標籤的處理主要包括了兩個部分:
1、屬性的更新:樣式、屬性、事件等。
2、子節點的更新:子節點內容、子節點自身、此部分設計diff演算法。

1、更新屬性

當執行mountComponent方法時,ReactDOMComponent首先會生成標記和標籤,通過方法:this. createOpenTagMarkupAndPutListeners(transaction) 來處理DOM節點的屬性和事件。

1、如果存在事件,則針對當前的節點新增事件代理:enqueuePutListener(this, propKey, propValue, transaction)
2、如果存在樣式,首先會對樣式進行合併操作,然後通過CSSPropertyOperations.createMarkupForStyles(propValue, this)方法建立樣式。
3、建立屬性:DOMPropertyOperations.createMarkupForProperty(propKey, propValue)
4、建立唯一識別符號:DOMPropertyOperations.createMarkupForID(this._domID)

當執行receiveComponent方法時,ReactDOMComponent會通過this.updateComponent來更新DOM節點屬性(真實DOM)。進行了如下兩方面的工作:
1、刪除不需要的舊屬性
2、更新新屬性

2、更新子節點

當執行mountComponent方法時,ReactDOMComponent會通過方法this._createContentMarkup (transaction, props, context)來處理DOM節點的內容。首先獲取節點內容,如果節點存在子節點,則通過方法this. mountChildren(childrenToUse, transaction, context)對子節點進行初始化渲染。

和更新屬性類似,當執行receiveComponent方法時,ReactDOMComponent會通過this._updateDOMChildren
(lastProps, nextProps, transaction, context)來更新DOM內容和子節點(真實DOM)。進行了如下兩方面的工作:
1、刪除不需要的子節點和內容
2、更新子節點和內容

自定義元件

自定義元件ReactCompositeComponent實現了一整套的React生命週期和setState機制,因此自定義元件實在生命週期環境中進行更新屬性、內容和子節點的操作。這些更新操作與ReactDOMComponent的操作類似。