【译】了解React源代码-初始渲染(简单组件)1
【译】了解React源代码-初始渲染(简单组件)2
【译】了解React源代码-初始渲染(简单组件)3
【译】了解React源代码-初始渲染(类组件)4
【译】了解React源代码-初始渲染(类组件)5
【译】了解React源代码-UI更新(事务)6
【译】了解React源代码-UI更新(事务)7
【译】了解React源代码-UI更新(单个DOM)8
【译】了解React源代码-UI更新(DOM树)9
在上一篇文章中,我们介绍了根据JSX表达式创建ReactCompositeComponent
实例的过程。 从batchedMountComponentIntoNode()
的角度出发,本文将继续介绍简单的组件渲染。
本文中使用的文件:
renderers / dom / client / ReactMount.js:
定义mountComponentIntoNode()
,本文逻辑过程的入口点以及用于DOM渲染的ReactDOMContainerInfo
renderers / shared / stack / reconciler / ReactReconciler.js:
调用各种ReactXXXComponent
的mountComponent
renderers / shared / stack / reconciler / ReactCompositeComponent.js:
调用mountComponent
来启动TopLevelWrapper
,并调用performInitialMount
来启动ReactDOMComponent
renderers / dom / shared / ReactDOMComponent.js:
定义ReactDOMComponent
本文中使用的完整静态调用层次结构:
|=ReactMount.render(nextElement, container, callback) ___
|=ReactMount._renderSubtreeIntoContainer() |
|-ReactMount._renderNewRootComponent() |
|-instantiateReactComponent() |
|~batchedMountComponentIntoNode() upper half
|~mountComponentIntoNode() (platform agnostic)
|-ReactReconciler.mountComponent() |
|-ReactCompositeComponent.mountComponent() |
|-ReactCompositeComponent.performInitialMount() |
|-instantiateReactComponent() _|_
|-ReactDOMComponent.mountComponent() lower half
|-_mountImageIntoNode() (HTML DOM specific)
_|_
batchedMountComponentIntoNode()
并没有做什么。它只是调用另一个函数调用mountComponentIntoNode()
。
For now let’s omit the delicacy of those indirect function calls and just see them as direct ones. I will cover transaction and batched updates in later articles.
现在,让我们忽略那些间接函数调用,只将它们视为直接函数调用。 我将在以后的文章中介绍转换和批量更新。
mountComponentIntoNode()-上下两部分的交点
mountComponentIntoNode()
是逻辑的交叉点,由平台不可知代码(在本文中称为“上半部分”)和特定于HTML的逻辑(下半部分)组成。 此案例的所有主要任务也都有效地在此函数(及其子调用)中完成,其中:
1)从顶级ReactCompositeComponent [T]
派生一个ReactDOMComponent
;
2)将ReactDOMComponent
呈现为真实的DOM元素( Element );
3)将该元素插入文档( Document )对象中。
function mountComponentIntoNode(
wrapperInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -----> document.getElementById("root")
transaction, // scr: -----> not of interest
shouldReuseMarkup, // scr: -----> null
context, // scr: -----> emptyObject
) {
...
var markup = ReactReconciler.mountComponent( // scr: -----> 1),2)
wrapperInstance,
transaction,
null,
ReactDOMContainerInfo(wrapperInstance, container),
context,
0 /* parentDebugID */,
);
...
ReactMount._mountImageIntoNode( // scr: -----> 3)
markup,
container,
wrapperInstance,
shouldReuseMarkup,
transaction,
);
ReactMount@renderers/dom/client/ReactMount.js
其中,1)仍作为上半部分的高级操作进行,而2),3)是具体的DOM操作。 2)完成后,我们将能够在屏幕上看到元素<h1 style = {“ color”:“ blue”}> hello world </ h1>
。
对于1),ReactReconciler.mountComponent()
是另一个简单的函数,它调用传递给它的internalInstance
的相应mountComponent()
,在这种情况下为ReactCompositeComponent [T]
。
mountComponent: function(
internalInstance,
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID, // 0 in production and for roots
) {
var markup = internalInstance.mountComponent(
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID,
);
... // scr: transaction related code
return markup;
},
ReactReconciler@renderers/shared/stack/reconciler/ReactReconciler.js
这里的一个特殊参数是ReactDOMContainerInfo
,它在传递给ReactReconciler.mountComponent()
的同时被构造:
function ReactDOMContainerInfo(topLevelWrapper, node) {
var info = {
_topLevelWrapper: topLevelWrapper, // scr: -------------------> ReactCompositeComponent[T]
_idCounter: 1,
_ownerDocument: node ? node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : null, // scr: -----> node.nowerDocument
_node: node, // src: -----> document.getElementById("root")
_tag: node ? node.nodeName.toLowerCase() : null, // scr: -----> 'div'
_namespaceURI: node ? node.namespaceURI : null // scr: ----->element.namespaceURI
};
... // scr: DEV code
return info;
}
ReactDOMContainerInfo@renderers/dom/client/ReactMount.js
结果对象ReactDOMContainerInfo [ins]
将由3)使用。
这是由过渡功能组成的尽头。 现在,我们进入下一个重要的站点。
ReactCompositeComponent.mountComponent()-初始化ReactCompositeComponent [T]
This is where the magic, I mean, the reaction happens
我的意思是,这就是魔术发生的地方
在上一步中,仅对ReactCompositeComponent [T]
的_currentElement
填充了对ReactElement [2]
的引用,这使对象有点沉闷。 但现在不再是。 该属性将依次用于触发ReactCompositeComponent [T]
中的“反应”,并将对象转换(初始化)为更有意义的对象。
此步骤的指定数据结构为:
数据结构.png实际使用的调用栈为:
ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
|-ReactMount._renderNewRootComponent()
|-instantiateReactComponent()
|~batchedMountComponentIntoNode(
componentInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -> document.getElementById("root")
shouldReuseMarkup, // scr: -----> null
context, // scr: -----> emptyObject
)
|~mountComponentIntoNode(
wrapperInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -----> same
transaction, // scr: -----> not of interest
shouldReuseMarkup, // scr: ---> same
context, // scr: -----> not of interest
)
|-ReactReconciler.mountComponent(
internalInstance, // scr: --> ReactCompositeComponent[T]
transaction, // scr: --> not of interest
hostParent, // scr: --> null
hostContainerInfo,// scr: --> ReactDOMContainerInfo[ins]
context, // scr: --> not of interest
parentDebugID, // scr: --> 0
)
/* we are here */
|-ReactCompositeComponent[T].mountComponent(same)
接下来,让我们看一下ReactCompositeComponent.mountComponent()
的实现。
I will not list the implementation for small functions (when they are get called) but give return value directly for the sake of concision.
我不会列出小型函数的实现(当它们被调用时),但是为了简洁起见,直接给出返回值。
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
// scr: this ------> ReactCompositeComponent[T]
) {
// scr: --------------------------------------------------------> 1)
this._context = context; // scr: -----> emptyObject
this._mountOrder = nextMountID++; // scr: ----------------------> global veriable, accumulative
this._hostParent = hostParent; // scr: -----> null
this._hostContainerInfo = hostContainerInfo; // scr: -----------> ReactDOMContainerInfo[ins]
var publicProps = this._currentElement.props; // scr: ----------> { child: ReactElement[1] }
var publicContext = this._processContext(context); // scr: -----> meaning less, emptyObject
// scr: --------------------------------------------------------> 2)
var Component = this._currentElement.type; // scr: -------------> TopLevelWrapper
var updateQueue = transaction.getUpdateQueue(); // scr: --------> not of interest
// Initialize the public class
var doConstruct = shouldConstruct(Component); // scr: ----------> true, for TopLevelWrapper.prototype.isReactComponent = {};
var inst = this._constructComponent(
doConstruct,
publicProps,
publicContext,
updateQueue,
); // scr: ----------> call TopLevelWrapper’s constructor
var renderedElement;
// Support functional components
if (!doConstruct && (inst == null || inst.render == null)) {
...
} else {
if (isPureComponent(Component)) { // scr: --------------------> TopLevelWrapper.prototype.isPureReactComponent is not defined
...
} else {
this._compositeType = CompositeTypes.ImpureClass;
}
}
// scr: --------------------------------------------------------> 3)
// 这些应该在构造函数中设置,但是为了简化类抽象,我们在事后设置它们。
inst.props = publicProps; // scr: ----> { child: ReactElement[1] }
...
// scr: --------------------------------------------------------> 4)
this._instance = inst; // scr: ---------------------------------> link the ReactCompositeComponent[T] to the TopLevelWrapper instance
// 将实例中的引用存储回内部表示形式
ReactInstanceMap.set(inst, this); // scr: ----------------------> link the TopLevelWrapper instance back to ReactCompositeComponent[T]
...
var markup;
if (inst.unstable_handleError) { // scr: -----------------------> false, TopLevelWrapper.prototype.unstable_handleError is not defined
...
} else {
// scr: --------------------------------------------------------> 5)
markup = this.performInitialMount( // scr: a initial at the end?
renderedElement,
hostParent,
hostContainerInfo,
transaction,
context,
);
}
...
return markup;
}
ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
在ReactCompositeComponent [T]
的上下文中运行(作为成员函数),此方法
1)将参数直接分配给ReactCompositeComponent [T]
和局部变量的相应属性。 这里有趣的一个变量是publicProps
。 它将很快使用。
2)从this._currentElement.type
中提取TopLevelWrapper
,并调用其构造函数来创建TopLevelWrapper
实例。 在此过程中:
shouldConstruct(Component)
检查TopLevelWrapper.prototype.isReactComponent
是否设置,如果设置,则返回true
。 和
this._constructComponent()
如果上一个函数的结果为true
,则调用TopLevelWrapper
构造函数。 这次,我将实例命名为TopLevelWrapper [ins]
。
This is the time you may want to check the definition of TopLevelWrapper in the previous post, search for ***.
这是您可能要检查上一篇文章中TopLevelWrapper的定义的时间,搜索***。
3)使用1)中的publicProps
使用先前存储在包装元素ReactElement [2]
中的信息来初始化新实例的TopLevelWrapper [ins] .props
;
4)在此(ReactCompositeComponent [T]
)和TopLevelWrapper [ins]
之间创建一个双向链接。 使用this._instance
创建一个链接,使用ReactInstanceMap
创建另一个链接。 this._instance
将在下一步中很快使用,ReactInstanceMap
将在以后的文章中使用。
I type 4 stars **** here as you might need to come back to check ReactInstanceMap‘s origin.
我在这里输入4星****,因为您可能需要返回检查ReactInstanceMap的来源。
5)转到下一步。
ReactCompositeComponent.performInitialMount()-从ReactElement [1]创建一个ReactDOMComponent
此步骤将剥离包装并创建一个ReactDOMComponent
实例。 另外,我们将在第一时间看到ReactHostComponent
。 该组件用于将函数调用从上半部分链接到下半部分。
调用栈:
ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
|-ReactMount._renderNewRootComponent()
|-instantiateReactComponent()
|~batchedMountComponentIntoNode()
|~mountComponentIntoNode()
|-ReactReconciler.mountComponent()
|-ReactCompositeComponent[T].mountComponent(same)
/* we are here */
|-ReactCompositeComponent[T].performInitialMount(
renderedElement, // scr: -------> undefined
hostParent, // scr: -------> null
hostContainerInfo, // scr: -------> ReactDOMContainerInfo[ins]
transaction, // scr: -------> not of interest
context, // scr: -------> not of interest
)
ReactCompositeComponent.performInitialMount()
做三件事。
1)如前所述提取ReactElement [1]
;
2)根据ReactElement [1] .type
实例化一个ReactDOMComponent
;
3)调用ReactDOMComponent.mountComponent()
渲染DOM元素。
why a performInitialMount is invoked at the end of the mountComponent function? Because it is “initial” for the next mountComponent
为什么在mountComponent函数的末尾调用performInitialMount? 因为它是下一个mountComponent的“初始”
performInitialMount: function(
renderedElement,
hostParent,
hostContainerInfo,
transaction,
context,
) {
var inst = this._instance;
var debugID = 0;
if (inst.componentWillMount) { // scr: ----------> undefined
…
}
// scr: ------------------------------------------------------> 1)
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent(); // scr: ---> calls TopLevelWrapper.render() to extract ReactElement[1].
}
// scr: ------------------------------------------------------> 2)
var nodeType = ReactNodeTypes.getType(renderedElement); // scr: -> ReactNodeTypes.HOST
this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(
renderedElement,
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */,
);
this._renderedComponent = child;
// scr: ------------------------------------------------------> 3)
var markup = ReactReconciler.mountComponent(
child,
transaction,
hostParent,
hostContainerInfo,
this._processChildContext(context),
debugID,
);
return markup;
},
ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
现在,我对每个步骤进行详细说明:
1)this._renderValidatedComponent()
只需调用TopLevelWrapper.render()
,以便将从TopLevelWrapper [T] .props.child
中提取的ReactElement [1]
分配给renderElement
。
2)this._instantiateReactComponent()
是上一篇文章中讨论的_instantiateReactComponent@renderers/shared/stack/reconciler/instantiateReactComponent.js
的别名。 但是,这次使用renderedElement
(即ReactElement [1]
)创建一个ReactDOMComponent
实例…
在这儿等着,
如果我们再次查看_instantiateReactComponent()
的实现
...
// Special case string values
if (typeof element.type === ‘string’) { // scr: -------> this time
instance = ReactHostComponent.createInternalComponent(element);
}
...
_instantiateReactComponent@renderers/shared/stack/reconciler/instantiateReactComponent.js
之所以调用ReactHostComponent
的createInternalComponent()
是因为ReactElement [1] .type
是“ h1
”,它实例化了genericComponentClass
:
function createInternalComponent(element) {
...
return new genericComponentClass(element);
}
ReactHostComponent@renderers/shared/stack/reconciler/ReactHostComponent.js
但是为什么它与ReactDOMComponent
有关系呢?
实际上,在编译期间,特定于平台的组件ReactDOMComponent
作为genericComponentClass
被“注入”到ReactHostComponent
。 目前,我们认为链接已经建立,注入机制将在以后的文章中讨论。
I type *5 here.
我在这里输入* 5。
ReactDOMComponent
的构造函数与ReactCompositeComponent
的构造函数非常相似:
function ReactDOMComponent(element) {
var tag = element.type; // scr: --------> 'h1'
...
this._currentElement = element; // scr: --------> ReactElement[1]
this._tag = tag.toLowerCase(); // scr: --------> 'h1'
... // scr: default values, null, 0, etc.
}
ReactDOMComponent@renderers/dom/shared/ReactDOMComponent.js
我们将返回的实例命名为ReactDOMComponent [ins];
3)已经覆盖了ReactReconciler.mountComponent()
,它仅调用第一个参数的mountComponent()
,在这种情况下为ReactDOMComponent [ins]
。
现在,逻辑过程降到下半部分。
未完待续…
网友评论