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的操作類似。