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

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

作者: nextChallenger | 来源:发表于2019-10-14 10:12 被阅读0次

    【译】了解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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/xaccmctx.html