Virtual DOM模型
1.Virtual DOM模型負責Virtual DOM底層框架的構建工作,它擁有一整套的Virtual DOM標籤,
並負責虛擬節點及其屬性的構建,更新,刪除等工作.
2.其實,構建一套簡易Virtual DOM模型並不複雜,它只需要具備一個DOM標籤所需的基本元素即可.
{
// 標籤名
tagName: `div`,
// 屬性
properties: {
// 樣式
style: {}
},
// 子節點
children: [],
// 唯一標識
key: 1
}
3.Virtual DOM中的節點稱為ReactNode,它分為3種型別:ReactElement,ReactFragment,ReactText.
其中,ReactElement又分為ReactComponentElement和ReactDOMElement.
建立React元素
// 輸入jsx
const app = <Nav color="blue"><Profile>click</Profile></Nav>;
// 輸出js
const app = React.createElement(
Nav,
{color: `blue`},
React.createElement(Profile, null, `click`)
);
通過jsx建立的虛擬元素最終會被編譯成呼叫React的createElement方法
// createElement只是做了簡單修正,返回一個ReactElement例項物件
// 也就是虛擬元素的例項
ReactElement.createElement = function(type, config, children) {
// 初始化引數
var propName;
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
// 如果存在config,則提取裡面的內容
if (config != null) {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : `` + config.key;
self = config._self === undefined ? null : config._self;
source = config._source === undefined ? null : config._source;
// 複製config裡的內容到props(id和className等)
for (propName in config) {
if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
}
// 處理children,全部掛載到props的children屬性上,如果只有一個引數,直接賦值給children
// 否則做合併處理
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 如果某個prop為空且存在預設的prop,則將預設prop賦給當前的prop
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (typeof props[propName] === `undefined`) {
props[propName] = defaultProps[propName]
}
}
}
// 返回一個ReactElement例項物件
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
初始化元件入口
1.當使用React建立元件時,首先會呼叫instantiateReactComponent,這就是初始化元件的入口函式,
它通過判斷node型別來區分不同元件的入口.
// 初始化元件入口
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 {
// 自定義元件
instance = new ReactCompositeComponentWrapper();
}
} else if (typeof node === `string` || typeof node === `number`) {
// 字串或數字
instance = ReactNativeComponent.createInstanceForText(node);
} else {
// 不做處理
}
// 設定例項
instance.construct(node);
// 初始化引數
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
文字元件
1.當node型別為文字節點時是不算Virtual DOM元素的,但React為了保持渲染的一致性,
將其封裝為文字元件ReactDOMTextComponent.
DOM標籤元件
1.Virtual DOM模型涵蓋了幾乎所有的原生DOM標籤,如<div>,<p>,<span>等.
當開發者使用React時,此時的<div>並不是原生的<div>標籤,他其實是React生成的
Virtual DOM物件,只不過標籤名稱相同罷了.
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
var ret = `<` + this._currentElement.type;
// 拼湊出屬性
for (var propKey in props) {
var propValue = props[propKey];
if (registrationNameModules.hasOwnProperty(propKey)) {
// 針對當前的節點新增事件代理
if (propValue) {
enqueuePutListener(this, propKey, propValue, transaction);
}
} else {
if (propKey === STYLE) {
if (propValue) {
// 合併樣式
propValue = this._previousStyleCopy = Object.assign({}, props.style);
}
propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
}
// 建立屬性標識
var markup = null;
if (this._tag != null && isCustomComponent(this._tag, props)) {
markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
}
if (markup) {
ret += ` ` + markup;
}
}
}
// 對於靜態頁面,不需要設定react-id,這樣可以節省大量位元組
if (transaction.renderToStaticMarkup) {
return ret;
}
// 設定reactid
if (!this._nativeParent) {
ret += ` ` + DOMPropertyOperations.createMarkupForRoot();
}
ret += ` ` + DOMPropertyOperations.createMarkupForID(this._domID);
return ret;
}