一: render
参数 | 描述 |
---|---|
nextElement | 要挂载的元素 |
container | 挂载点 |
callback | 回调方法 |
二: _renderSubtreeIntoContainer
参数 | 描述 |
---|---|
parentComponent | 父组件 |
nextElement | 要挂载的元素 |
container | 挂载点 |
callback | 回调方法 |
大家来看一下这个方法的源码:(有部分代码不是分析的范畴所以去掉了)
_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement
});
var nextContext;
if (parentComponent) {
var parentInst = ReactInstanceMap.get(parentComponent);
nextContext = parentInst._processChildContext(parentInst._context);
} else {
nextContext = emptyObject;
}
var prevComponent = getTopLevelWrapperInContainer(container);
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props.child;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
var publicInst = prevComponent._renderedComponent.getPublicInstance();
var updatedCallback = callback && function () {
callback.call(publicInst);
};
ReactMount._updateRootComponent(prevComponent, nextWrappedElement, nextContext, container, updatedCallback);
return publicInst;
} else {
ReactMount.unmountComponentAtNode(container);
}
}
var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
var containerHasNonRootReactChild = hasNonRootReactChild(container);
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
复制代码
从上边代码可以看出这个方法主要是生成了 ReactMount._renderNewRootComponent所需要的几个参数
- nextWrappedElement // 最终就是把nextElement作为TopLevelWrapper组件props.children
- container
- shouldReuseMarkup // 是否应该重新标记
- nextContext // 第一次进入是null
三:ReactMount._renderNewRootComponent
看这个方法的源码:
_renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
var componentInstance = instantiateReactComponent(nextElement, false);
ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);
var wrapperID = componentInstance._instance.rootID;
instancesByReactRootID[wrapperID] = componentInstance;
return componentInstance;
},
复制代码
首先通过instantiateReactComponent获取到组件的实例,那么我们继续追踪instantiateReactComponent
instantiateReactComponent源码:
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
var type = element.type;
if (typeof type !== 'function' && typeof type !== 'string') {
var info = '';
// Special case string values
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
// We renamed this. Allow the old name for compat. :(
if (!instance.getHostNode) {
instance.getHostNode = instance.getNativeNode;
}
} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
instance._mountIndex = 0;
instance._mountImage = null;
if (process.env.NODE_ENV !== 'production') {
instance._debugID = shouldHaveDebugID ? getNextDebugID() : 0;
}
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (process.env.NODE_ENV !== 'production') {
if (Object.preventExtensions) {
Object.preventExtensions(instance);
}
}
return instance;
}
复制代码
instantiateReactComponent方法其实就是对组件类型的判断,有以下几种情况
- 如果节点是空的或则没有就用ReactEmptyComponent
- 如果节点是对象(接下来就判断节点的type,这个type实际就是React.createElement返回的element的type)
- 如果节点的type是string->ReactHostComponent.createInternalComponent(element),实际就是我们写的真实dom,在这里会处理样式和事件
- 如果节点的type是组件内部的类型话-> new element.type(element)
- 如果节点的type是除了以上情况类型的话->new ReactCompositeComponentWrapper(element) // 这个类型就是我们平时写的组件
- 如果节点是文本或数字的话ReactHostComponent.createInstanceForText(node)
四:batchedMountComponentIntoNode
看源码:
function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* useCreateElement */
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement);
transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
复制代码
这个方法就是是批量挂载组件到container上,在这个方法中我们发现有一个关键的方法mountComponentIntoNode,那么我么接下继续追踪这个方法
mountComponentIntoNode
function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReuseMarkup, context) {
var markerName;
if (ReactFeatureFlags.logTopLevelRenders) {
var wrappedElement = wrapperInstance._currentElement.props.child;
var type = wrappedElement.type;
markerName = 'React mount: ' + (typeof type === 'string' ? type : type.displayName || type.name);
console.time(markerName);
}
var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0 /* parentDebugID */
);
if (markerName) {
console.timeEnd(markerName);
}
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
ReactMount._mountImageIntoNode(markup, container, wrapperInstance, shouldReuseMarkup, transaction);
}
复制代码