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

【译】了解React源代码-初始渲染(类组件)4

作者: nextChallenger | 来源:发表于2019-10-15 11:29 被阅读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


我们已经完成了一个简单组件的渲染过程。 这次,我们将通过讨论如何呈现类组件(我们可能在日常开发中使用的典型组件)来探索该过程的更多分支。

本文中使用的文件:与第一篇第二篇相同

I use {} to reference the previous post that is relevant to the methods (or logic process) being discussed.
我使用{}来引用与所讨论的方法(或逻辑过程)相关的先前文章。

名为App的组件与我在第一篇文章开头提供的组件相似,在该组件中,我们认为它对于初学者来说太复杂了。 但是,由于我们已经进行了一些升级,因此看起来不再那么令人生畏。

import React, { Component } from ‘react’;
import logo from ‘./logo.svg’;
import ‘./App.css’;

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      desc: 'start',
    };
  }

  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src="main.jpg" className="App-logo" alt="logo" />
          <h1> "Welcom to React" </h1>
        </div>
        <p className="App-intro">
          { this.state.desc }
        </p>
      </div>
    );
  }
}

export default App;

App@App.js

如前所述,使用以下组件呈现上述组件:

ReactDOM.render(
  <App />,
  document.getElementById(‘root’)
);

现在,转义后的代码:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      desc: 'start',
    };
  }

  render() {
    return React.createElement(
      'div',
      { className: 'App' },
      React.createElement(
        'div',
        { className: 'App-header' },
        React.createElement(
          'img',
          { src: "main.jpg", className: 'App-logo', alt: 'logo' }
        ),
        React.createElement(
          'h1',
          null,
          ' "Welcom to React" '
        )
      ),
      React.createElement(
        'p',
        { className: 'App-intro' },
        this.state.desc
      )
    );
  }
}

export default App;

...

ReactDOM.render(React.createElement(App, null), document.getElementById('root'));

在这里,我们将Component视为常见的基类,因为本文不会使用其他方法。

这次,我们可以快速理解与简单组件共享的逻辑。

构造顶层包装容器ReactCompositeComponent [T]

指定的数据结构:

此步骤与简单组件渲染中的步骤几乎相同,因此我仅作简要说明,它

1)使用ReactElement.createElement(type,config,children)创建ReactElement [1](这次将App传递给type,并且configchildrennull);

2)在_renderSubtreeIntoContainer()中创建ReactElement [2]

3)使用InstantiateReactComponent()创建指定的包装。

ReactElement.createElement(type,    // scr: -------------> App
                           config,  // scr: -------------> null
                           children // scr: -------------> null
) // scr: ------------------------------------------------------> 1)
ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer(
   parentComponent, // scr: ----> null
   nextElement,     // scr: ----> ReactElement[1]
   container,       // scr: ----> document.getElementById(‘root’)
   callback’        // scr: ----> undefined
) // scr: ------------------------------------------------------> 2)
  |-instantiateReactComponent( // scr: -------------------------> 3)
      node, // scr: ------> ReactElement[2]
      shouldHaveDebugID /* false */
    )
    |-ReactCompositeComponentWrapper(
        element // scr: ------> ReactElement[2]
      );
    |=ReactCompositeComponent.construct(element /* same */)

这就是我们在{第一篇}中讨论的内容。

初始化ReactCompositeComponent [T]

指定的数据结构:

步骤也相同:

1)ReactDOMContainerInfo [ins]代表容器DOM元素document.getElementById('root');

2)实例化TopLevelWrapperTopLevelWrapper [ins]),并将其设置为ReactCompositeComponent [T] ._ instance以及其他属性的初始化;

3)同样,mountComponentIntoNode是上半部分和下半部分的交叉点,其中ReactCompositeComponent [T] .mountComponent返回一个完整的DOMLazyTreeReactMount._mountImageIntoNode(下半部分的一种方法)可以使用它。

ReactDOM.render                                           ___
|=ReactMount.render(nextElement, container, callback)      |
|=ReactMount._renderSubtreeIntoContainer()                 |
  |-ReactMount._renderNewRootComponent()                   |
  |-instantiateReactComponent()                            |
    |~batchedMountComponentIntoNode()                 upper half
      |~mountComponentIntoNode()                (platform agnostic)
        |-ReactReconciler.mountComponent() // scr-----> 1) |
          |-ReactCompositeComponent[T].mountComponent() scr:> 2)3)                    
            ...                                           _|_
               ...                                    lower half
        |-_mountImageIntoNode()                 (HTML DOM specific)

这就是我们在{第二篇}的第一部分中讨论的内容。

除了在参数值方面存在一些细微差异外,与顶层包装程序相关的操作与我们在先前文章中讨论的内容完全相同。 这些操作完成后,逻辑将处理到特定于类组件的第一个分支。

ReactCompositeComponent [T] .performInitialMount()-从ReactElement [1]创建一个ReactCompositeComponent

此步骤将剥离包装,并创建另一个ReactCompositeComponent实例以反映App组件。

指定的数据结构:

调用栈:

...
|~mountComponentIntoNode()                                    |
  |-ReactReconciler.mountComponent()                          |
    |-ReactCompositeComponent[T].mountComponent()             |
      /* we are here */                                       |
      |-ReactCompositeComponent[T].performInitialMount(       |
          renderedElement,   // scr: -------> undefined       |
          hostParent,        // scr: -------> null        upper half
          hostContainerInfo, // scr: ------->                 | ReactDOMContainerInfo[ins]                                    |
          transaction,       // scr: -------> not of interest |
          context,           // scr: -------> not of interest |
        )                                                     |

该过程与{post two}中的performInitialMount()非常相似。 唯一的区别是_instantiateReactComponent基于ReactElement [1] .type,为类组件(App)创建了ReactCompositeComponent而不是ReactDOMComponent。 简而言之:

1)它调用_renderValidatedComponent(),后者依次调用TopLevelWrapper.render()提取ReactElement [1]
2)用_instantiateReactComponent实例化一个ReactCompositeComponent(我们将对象命名为ReactCompositeComponent [ins])
3)通过ReactReconciler(递归)调用ReactCompositeComponent [ins] .mountComponent,然后进行下一步。

performInitialMount: function (
  renderedElement,
  hostParent,
  hostContainerInfo,
  transaction,
  context) 
{
  var inst = this._instance;

...

  if (inst.componentWillMount) {
... // scr: we did not define componentWillMount() in App
  }

// If not a stateless component, we now render
  if (renderedElement === undefined) {
    renderedElement = this._renderValidatedComponent(); // scr: > 1)
  }

  var nodeType = ReactNodeTypes.getType(renderedElement); // scr: -> the type is ReactNodeTypes.Composite this time

  this._renderedNodeType = nodeType;

  var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
  );      // scr: ----------------------------------------------> 2)

  this._renderedComponent = child;

  var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID); // scr: ----------------------------------------------> 3)

...// scr: DEV code

  return markup;
},

ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

ReactCompositeComponent [ins] .mountComponent()-初始化ReactCompositeComponent [ins]

指定的数据结构:

调用栈:

...
|~mountComponentIntoNode()                                    |
  |-ReactReconciler.mountComponent()                          |
    |-ReactCompositeComponent[T].mountComponent()             |
      |-ReactCompositeComponent[T].performInitialMount()  upper half
        |-ReactReconciler.mountComponent()                    |
          /* we are here */                                   |
          |-ReactCompositeComponent[ins].mountComponent(same)   |

ReactCompositeComponent [T] .mountComponent(){第二篇}相同,此步骤最重要的任务是使用ReactCompositeComponent [ins] ._ currentElement(ReactElement [1])实例化App

方法中执行此操作的行是:

...
var inst = this._constructComponent(
    doConstruct,
    publicProps,
    publicContext,
    updateQueue,
  );
...

ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

在其中调用App的构造函数。

...
  constructor(props) {
    super(props);
    this.state = {
      desc: 'start',
    };
  }
...

// copied from the beginning of this text

然后(我们将其命名)App [ins]设置为ReactCompositeComponent [ins] ._ instance,并且还通过ReactInstanceMap创建了反向链接。

This is the custom component App instance which responds to your this.setState(…).
这是响应您的this.setState(…)的自定义组件App实例。

其他操作包括:1)App [ins] .props参考ReactElement [1] .props; 和2)由于++对全局变量nextMountID进行操作,因此ReactCompositeComponent [ins] ._ mountOrder设置为2。

重要的是要注意App [ins] .render()是我们一开始定义的另一个App方法。 与TopLevelWrapper [ins] .render()返回具体的ReactElement实例不同,App [ins] .render()在调用React.createElement()时依赖于它。 我们将很快访问此方法。

由于此步骤与初始化ReactCompositeComponent [T] {第二篇}非常相似,因此我们不再进一步研究主力方法(即mountComponent())。

mountComponent: function(
  transaction,
  hostParent,
  hostContainerInfo,
  context,
// scr: this ------> ReactCompositeComponent[T]
) {
...
  this._mountOrder = nextMountID++; // scr: --------------------> 2)
...
  var publicProps = this._currentElement.props; // scr: -----------> { child: ReactElement[1] }
...
// scr: -------------------------------------------------> critical)
  var inst = this._constructComponent(
    doConstruct,
    publicProps,
    publicContext,
    updateQueue,
  ); // scr: ----------> call TopLevelWrapper’s constructor
  var renderedElement;
...
// These should be set up in the constructor, but as a convenience   
// for simpler class abstractions, we set them up after the fact.
// scr: --------------------------------------------------------> 1)
  inst.props = publicProps; // scr: ----> { child: ReactElement[1] }
…
  // scr: -----------------------------------------------> critical)
  this._instance = inst; // scr: ---------------------------------> link the ReactCompositeComponent[T] to the TopLevelWrapper instance
// Store a reference from the instance back to the internal representation
  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: -------------------------------------------------> next)
    markup = this.performInitialMount( // scr: a initial at the end?
      renderedElement,
      hostParent,
      hostContainerInfo,
      transaction,
      context,
    );
  }
…

  return markup;
}

ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

ReactCompositeComponent [ins] .performInitialMount()-创建一个ReactDOMComponent

...
|~mountComponentIntoNode()                                    |
  |-ReactReconciler.mountComponent()                          |
    |-ReactCompositeComponent[T].mountComponent()             |
      |-ReactCompositeComponent[T].performInitialMount()  upper half
        |-ReactReconciler.mountComponent()                    |
          /* we are here */                                   |
          |-ReactCompositeComponent[ins].mountComponent()       |
            |-this.performInitialMount()                      |
              |-this._renderValidatedComponent()              |
              |-instantiateReactComponent()                  _|_ 
              |-ReactDOMComponent[6].mountComponent()     lower half

我们再次来到这里:

performInitialMount: function (
  renderedElement,
  hostParent,
  hostContainerInfo,
  transaction,
  context) 
{
  var inst = this._instance;
...
if (inst.componentWillMount) {
... // scr: we did not define componentWillMount() in App
  }
// If not a stateless component, we now render
  if (renderedElement === undefined) {
    renderedElement = this._renderValidatedComponent(); // scr: > 1)
  }
  var nodeType = ReactNodeTypes.getType(renderedElement); // scr: -> the type is ReactNodeTypes.Host this time
  this._renderedNodeType = nodeType;
  var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
  );      // scr: ----------------------------------------------> 2)
  this._renderedComponent = child;
  var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID); // scr: ----------------------------------------------> 3)
...// scr: DEV code
  return markup;
},

ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

在创建ReactDOMComponent(我们知道这是处理DOM操作的类)之前,需要提取App [ins]中定义的ReactElement。 为此,下一行(在_renderValidatedComponent()中)调用App [ins] .render(){第二篇}

...
renderedElement = this._renderValidatedComponent();
...

然后App [ins] .render()触发

React.createElement()的级联调用

要了解ReactElement树的建立方式,首先让我们重新访问App.render()实现:

render() {
    return React.createElement(           // scr: -----------> 5)
      'div',
      { className: 'App' },
      React.createElement(                // scr: -----------> 3)
        'div',
        { className: 'App-header' },
        React.createElement(              // scr: -----------> 1)
          'img',
          { src: "main.jpg", className: 'App-logo', alt: 'logo' }
        ),
        React.createElement(              // scr: -----------> 2)
          'h1',
          null,
          ' "Welcom to React" '
        )
      ),
      React.createElement(                // scr: -----------> 4)
        'p',
        { className: 'App-intro' },
        this.state.desc
      )
    );
  }

// copied from the beginning of this text

在此代码段中,我还给出了createElement()的调用顺序,该调用顺序遵循一个非常简单的原则:在调用createElement()函数之前,应从左到右解析参数(使用createElement())。

然后,我们可以检查每个ReactElement的创建{第一篇}。

React.createElement( // scr: --------------------------------> 1)
  ‘img’,
  { src: "main.jpg", className: ‘App-logo’, alt: ‘logo’ }
),

创建ReactElement[2]:
并且

React.createElement( // scr: --------------------------------> 2)
  ‘h1’,
  null,
  ‘Welcome to React’
)

创建ReactElement[3]:

(现在解决了3的两个参数。)

React.createElement(                // scr: -----------> 3)
  'div',
  ReactElement[2],
  ReactElement[3]
),

创建ReactElement[4]:

React.createElement(                // scr: -----------> 4)
  'p',
  { className: 'App-intro' },
  this.state.desc
)

创建 ReactElement[5]:

(现在,5的参数已解决。)

return React.createElement(           // scr: -----------> 5)
  'div',
  { className: 'App' },
  ReactElement[4],
  ReactElement[5]
)

创建 ReactElement[6]:

结合在一起,我们得到了 renderedElement引用的元素树:

ReactCompositeComponent[ins]._instantiateReactComponent() — Create ReactDOMComponent[6]

指定的数据结构:

在最后一步中创建的元素树用于通过以下行(在_instantiateReactComponent()内)创建ReactDOMComponent [6]{第二篇}

var child = this._instantiateReactComponent(
  renderedElement,
  nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */,
);

现在,ReactReconciler.mountComponent()调用ReactDOMComponent [6]mountComponent(),并且逻辑处理到下半部分。

未完待续…

(原文地址)Understanding The React Source Code - Initial Rendering (Class Component) IV

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

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

相关文章

网友评论

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

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