JSX解析
1.jsx建立的虛擬元素會被編譯成React的createElement方法
var ReactElement = function(type, key, ref, self, source, owner, props) {
var element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
return element;
};
ReactElement.createElement = function(type, config, children) {
var propName;
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
for (propName in config) {
if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
}
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;
}
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, );
};
複製程式碼
2.建立元件
使用React建立元件,會先呼叫instantiateReactComponent, 這是初始化元件的入口,通過判斷引數node的型別來區分建立什麼型別的元件
- 當node為空時,說明node不存在,則初始化一個空的元件. ReactEmptyComponent.create(instantiateReactComponent)。
- 當node型別為物件時,那麼就是DOM標籤元件或者說是自定義元件,
如果element型別(element = node,element.type='string')為字串,就會初始化一個dom標籤元件:ReactNativeComponent.createInternalComponent(element),
否則就會初始化一個自定義元件 ReactCompositeComponentWrapper()
- 當node型別為字串或者數字時,則初始化文字元件,ReactNativeComponent.createInstanceForText(node)。
- 其餘情況不做處理
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
}
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
複製程式碼
2.1文字元件
- 根據transaction.useCreateElement判斷文字是不是通過createElement建立的節點
- 是,則為這個節點建立標籤和標識domID就可以參與虛擬dom的diff
- 如果不是就直接返回文字
var ReactDOMTextComponent = function(text) {
this._currentElement = text;
this._stringText = '' + text;
this._hostNode = null;
this._hostParent = null;
this._domID = 0;
this._mountIndex = 0;
this._closingComment = null;
this._commentNodes = null;
};
Object.assign(ReactDOMTextComponent.prototype, {
mountComponent: function(transaction, hostParent, hostContainerInfo, context, ) {
var domID = hostContainerInfo._idCounter++;
var openingValue = ' react-text: ' + domID + ' ';
var closingValue = ' /react-text ';
this._domID = domID;
this._hostParent = hostParent;
if (transaction.useCreateElement) {
var ownerDocument = hostContainerInfo._ownerDocument;
var openingComment = ownerDocument.createComment(openingValue);
var closingComment = ownerDocument.createComment(closingValue);
var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
if (this._stringText) {
DOMLazyTree.queueChild(
lazyTree,
DOMLazyTree(ownerDocument.createTextNode(this._stringText)),
);
}
DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
ReactDOMComponentTree.precacheNode(this, openingComment);
this._closingComment = closingComment;
return lazyTree;
} else {
var escapedText = escapeTextContentForBrowser(this._stringText);
if (transaction.renderToStaticMarkup) {
return escapedText;
}
return ( '<!--' + openingValue + '-->' + escapedText + '<!--' + closingValue + '-->' );
}
},
receiveComponent: function(nextText, transaction) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
var commentNodes = this.getHostNode();
DOMChildrenOperations.replaceDelimitedText(
commentNodes[0],
commentNodes[1],
nextStringText,
);
}
}
},
});
複製程式碼
2.2 DOM標籤元件
- 虛擬dom涵蓋了原生的DOM標籤,使用div不是原div,他是一個虛擬dom物件,標籤名相同
- 屬性更新:更新樣式,更新屬性,處理事件
- 子節點更新:更新內容,更新子節點,
屬性更新
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
var ret = '<' + this._currentElement.type;
for (var propKey in props) {
if (!props.hasOwnProperty(propKey)) {
continue;
}
var propValue = props[propKey];
if (propValue == null) {
continue;
}
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)) {
if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey,propValue,);
}
} else {
markup = DOMPropertyOperations.createMarkupForProperty(propKey,propValue,);
}
if (markup) {
ret += ' ' + markup;
}
}
}
if (transaction.renderToStaticMarkup) {
return ret;
}
if (!this._hostParent) {
ret += ' ' + DOMPropertyOperations.createMarkupForRoot();
}
ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);
return ret;
},
複製程式碼
當執行receiveComponent方法時,通過this.updateComponent()更新DOM節點屬性
receiveComponent: function(nextElement, transaction, context) {
var prevElement = this._currentElement;
this._currentElement = nextElement;
this.updateComponent(transaction, prevElement, nextElement, context);
}
複製程式碼
在updateComponent中通過this._updateDOMProperties(lastProps, nextProps, transaction)更新DOM節點屬性
_updateDOMProperties: function(lastProps, nextProps, transaction) {
var propKey;
var styleName;
var styleUpdates;
for (propKey in lastProps) {
if ( nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null ) {
continue;
}
if (propKey === STYLE) {
var lastStyle = this._previousStyleCopy;
for (styleName in lastStyle) {
if (lastStyle.hasOwnProperty(styleName)) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
this._previousStyleCopy = null;
} else if (registrationNameModules.hasOwnProperty(propKey)) {
if (lastProps[propKey]) {
deleteListener(this, propKey);
}
} else if (isCustomComponent(this._tag, lastProps)) {
if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
DOMPropertyOperations.deleteValueForAttribute(getNode(this), propKey);
}
} else if (DOMProperty.properties[propKey] ||DOMProperty.isCustomAttribute(propKey)) {
DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey);
}
}
for (propKey in nextProps) {
var nextProp = nextProps[propKey];
var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps != null ? lastProps[propKey] : undefined;
if ( !nextProps.hasOwnProperty(propKey) || nextProp === lastProp || (nextProp == null && lastProp == null) ) {
continue;
}
if (propKey === STYLE) {
if (nextProp) {
nextProp = this._previousStyleCopy = Object.assign({}, nextProp);
} else {
this._previousStyleCopy = null;
}
if (lastProp) {
for (styleName in lastProp) {
if (lastProp.hasOwnProperty(styleName) &&(!nextProp || !nextProp.hasOwnProperty(styleName))) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
for (styleName in nextProp) {
if (nextProp.hasOwnProperty(styleName) &&lastProp[styleName] !== nextProp[styleName]) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
styleUpdates = nextProp;
}
} else if (registrationNameModules.hasOwnProperty(propKey)) {
if (nextProp) {
enqueuePutListener(this, propKey, nextProp, transaction);
} else if (lastProp) {
deleteListener(this, propKey);
}
} else if (isCustomComponent(this._tag, nextProps)) {
if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
DOMPropertyOperations.setValueForAttribute( getNode(this), propKey, nextProp, );
}
} else if ( DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey) ) {
var node = getNode(this);
if (nextProp != null) {
DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
} else {
DOMPropertyOperations.deleteValueForProperty(node, propKey);
}
}
}
if (styleUpdates) {
CSSPropertyOperations.setValueForStyles(getNode(this),styleUpdates,this,);
}
},
複製程式碼
更新子節點
在執行mountComponent方法時,通過this._createContentMarkup(transaction, props, context);處理DOM節點的內容
_createContentMarkup: function(transaction, props, context) {
var ret = '';
var innerHTML = props.dangerouslySetInnerHTML;
if (innerHTML != null) {
if (innerHTML.__html != null) {
ret = innerHTML.__html;
}
} else {
var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
var childrenToUse = contentToUse != null ? null : props.children;
if (contentToUse != null) {
ret = escapeTextContentForBrowser(contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren( childrenToUse, transaction, context, );
ret = mountImages.join('');
}
}
if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') {
return '\n' + ret;
} else {
return ret;
}
},
複製程式碼
當執行receiveComponent方法時,通過this._updateDOMChildren(lastProps, nextProps, transaction, context);更新DOM內容和子節點
_updateDOMChildren: function(lastProps, nextProps, transaction, context) {
var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;
var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;
var lastChildren = lastContent != null ? null : lastProps.children;
var nextChildren = nextContent != null ? null : nextProps.children;
var lastHasContentOrHtml = lastContent != null || lastHtml != null;
var nextHasContentOrHtml = nextContent != null || nextHtml != null;
if (lastChildren != null && nextChildren == null) {
this.updateChildren(null, transaction, context);
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
this.updateTextContent('');
}
if (nextContent != null) {
if (lastContent !== nextContent) {
this.updateTextContent('' + nextContent);
}
} else if (nextHtml != null) {
if (lastHtml !== nextHtml) {
this.updateMarkup('' + nextHtml);
}
} else if (nextChildren != null) {
this.updateChildren(nextChildren, transaction, context);
}
},
複製程式碼