【译】了解React源代码-初始渲染(简单组件)2

2019-10-14  本文已影响0人  nextChallenger

【译】了解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
调用各种ReactXXXComponentmountComponent

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

之所以调用ReactHostComponentcreateInternalComponent()是因为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]

现在,逻辑过程降到下半部分。

未完待续…

原文链接

(上一篇)【译】了解React源代码-初始渲染(简单组件)1

(下一篇)【译】了解React源代码-初始渲染(简单组件)3

上一篇下一篇

猜你喜欢

热点阅读