React中Virtual DOM几乎涵盖了全部的原生DOM。React大部分工做都是在Virtual DOM完成的。 ReactDOMComponent针对Virturl DOM主要进行了一下处理:html
当执行mountComponent时,ReactDOMComponent首先会生成标记和标签。经过createOpenTagMarkupAndPutListeners(transaction) 来处理DOM节点的属性和事件。node
源码以下:react
//ReactDOMComponent.js
//_createOpenTagMarkupAndPutListeners处理DOM节点的属性和事件
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
//声明ret变量,保存一个标签
var ret = '<' + this._currentElement.type;
//循环拼凑出属性
for (var propKey in props) {
//判断属性是不是props的实例属性,若是不是则跳出
if (!props.hasOwnProperty(propKey)) {
continue;
}
var propValue = props[propKey];
if (propValue == null) {
continue;
}
if (registrationNameModules.hasOwnProperty(propKey)) {
if (propValue) {
//经过enqueuePutListener添加事件代理
enqueuePutListener(this, propKey, propValue, transaction);
}
} else {
if (propKey === STYLE) {
//若是是样式属性的话则合并样式
if (propValue) {
propValue = this._previousStyleCopy = Object.assign({}, props.style);
}
//这里调用CSSPropertyOperations.createMarkupForStyles
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;
}
}
}
// For static pages, no need to put React ID and checksum. Saves lots of
// bytes.
//对于静态页,不须要设置react-id
if (transaction.renderToStaticMarkup) {
return ret;
}
//这里设置惟一标识
if (!this._nativeParent) {
ret += ' ' + DOMPropertyOperations.createMarkupForRoot();
}
ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);
return ret;
},
复制代码
当执行recevieComponent方法时,ReactDOMComponent会经过this.updateComponent来更新DOM节点属性。bash
- 若是不须要旧样式,则遍历旧样式集合,并对每一个样式进行置空删除
- 若是不须要事件,则将其事件监听的属性去掉,即针对当前的节点取消事件代理:deleteListener(this, propKey)
- 若是旧属性不在新属性集合里时,则须要删除旧属性:DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey)
- 若是存在新样式,则将新样式进行合并。
- 若是在旧样式中可是不在新样式中,则清除该样式.
- 若是既在旧样式中也在新样式中,且不相同,则更新该样式styleUpdates[styleName] = nextProp[styleName]
- 若是在新样式中,但不在旧样式中,则直接更新为新样式styleUpdates = nextProp
- 若是存在事件更新,则添加事件监听的属性enqueuePutListener(this, propKey, nextProp, transaction)
- 若是存在新属性,则添加新属性, 或者更新旧的同名属性DOMPropetyOperations.setValueForAttribute(node, propKey, nextProp)
//更新属性
_updateDOMProperties: function(lastProps, nextProps, transaction) {
var propKey;
var styleName;
var styleUpdates;
//循环遍历旧属性,当旧属性不在新属性集合里面的时候则要删除
for (propKey in lastProps) {
//若是新属性实例对象上有这个propKey 或者 propKey在旧属性的原型上,则直接跳过,这样剩下的都是不在
//新属性集合里面的,则都要删除
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 (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
//从DOM上删除没必要要的属性
DOMPropertyOperations.deleteValueForProperty(getNode(this), propKey);
}
}
//针对新属性,须要加到DOM节点上
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;
}
//DOM上写入新样式
if (propKey === STYLE) {
if (nextProp) {
if (__DEV__) {
checkAndWarnForMutatedStyle(
this._previousStyleCopy,
this._previousStyle,
this
);
this._previousStyle = nextProp;
}
nextProp = this._previousStyleCopy = Object.assign({}, nextProp);
} else {
this._previousStyleCopy = null;
}
if (lastProp) {
// Unset styles on `lastProp` but not on `nextProp`.
//在旧样式中且不在新样式中,且不相同,则更新该样式
for (styleName in lastProp) {
if (lastProp.hasOwnProperty(styleName) &&
(!nextProp || !nextProp.hasOwnProperty(styleName))) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
// Update styles that changed since `lastProp`.
for (styleName in nextProp) {
if (nextProp.hasOwnProperty(styleName) &&
lastProp[styleName] !== nextProp[styleName]) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
//不存在旧样式,则直接写入新样式
// Relies on `updateStylesByID` not mutating `styleUpdates`.
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 we're updating to null or undefined, we should remove the property // from the DOM node instead of inadvertently setting to a string. This // brings us in line with the same behavior we have on initial render. if (nextProp != null) { DOMPropertyOperations.setValueForProperty(node, propKey, nextProp); } else { //若是更新为null或者undefined,则执行删除属性操做 DOMPropertyOperations.deleteValueForProperty(node, propKey); } } } //若是styleUpdates不为空,则设置新样式 if (styleUpdates) { CSSPropertyOperations.setValueForStyles( getNode(this), styleUpdates, this ); } }, 复制代码
在mountComponent的时候,ReactComponent经过this.createContentMarkup处理DOM节点。 首先,获取节点内容props.dangerourslySetInnerHTML,若是存在子节点,则经过this.mountChildren对子节点进行初始化渲染。dom
//ReactComponent.js
//_createContentMarkup
_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) {
// TODO: Validate that text is allowed as a child of this node
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,ReactComponet会经过updateDOMChildren来更新DOM内容和节点。ui
预览地址 this