React Rendering Process

  1. The program assumes the following jsx

class Form extends React.Component {
  constructor() {
    super();
  }
  render() {
    return (
        <form>
          <input type="text"/>
        </form>
    );
  }
}

ReactDOM.render( (
  <div className="test">
    <span onClick={function(){}}>CLICK ME</span>
    <Form/>
  </div>
), document.getElementById('main'))
  1. Take the part of ReactDOM render (<div className="test"> </div>) as an example, and convert jsx to js with babel, the following code is obtained:

React.createElement( 'div', {
  className: 'test'
  },
  React.createElement( 'span',
    { onClick: function(){} },
    'CLICK ME'
  ),
  React.createElement(Form, null)
)
  1. Here's the api: React. createElement (component, props,... Children). It generates an object of js, which is used to represent a real dom node. The object of JS is what we call virtual dom. Virtual DOM means to simulate the DOM structure in html by using JS object structure. Batch add-delete and change-check first directly operates JS object, and then updates it to the real DOM tree. Because direct manipulation of JS objects is faster than those APIs that operate dom.) For example, the root element < div className= "test"> </div> generates the corresponding virtual dom:

{
  type: 'div',
  props: {
    className: 'test',
    children: []
  }
}

In addition to some dom-related attributes, virtual dom objects also include attributes that React itself needs, such as ref, key. The virtual dom generated by jsx in the final example is roughly as follows:

{
  type: 'div',
  props: {
    className: 'xxx',
    children: [ {
      type: 'span',
      props: {
        children: [ 'CLICK ME' ]
      },
      ref:
      key:
    }, {
      type: Form,
      props: {
        children: []
      },
      ref:
      key:
    } ] | Element
  }
  ref: 'xxx',
  key: 'xxx'
}
  1. With virtual dom, the next task is to render the virtual DOM tree into a real DOM tree. React's approach is to construct corresponding rendering objects for different type s. The rendering objects provide a mountComponent method (responsible for generating a specific dom node from a corresponding virtual dom node), and then iterate the whole vdom tree to generate a complete dom node tree, finally insert the container node. Look at the source code and you will find the following code:

// vdom is a virtual dom object generated in step 3
var renderedComponent = instantiateReactComponent( vdom );
// dom node
var markup = renderedComponent.mountComponent();
// Insert the generated dom node into the container node and display it on the page.
// Here's pseudo code. React's dom operation is encapsulated in DOMLazyTree
containerNode.appendChild( markup );

The instantiateReactComponent passes in a virtual dom node. This method does this by calling the following methods according to different type s:

// If the node is a string or a number
return ReactHostComponent.createInstanceForText( vdom(string|number) );
// If a node is a host built-in node, such as a browser's html node
return ReactHostComponent.createInternalComponent( vdom );
// If it is a React component node
return new ReactCompositeComponentWrapper( vdom );

ReactHostComponent.createXXX is also an abstraction, not the final rendering object, which shields the host. For example, ReactHostComponent. createInternalComponent (vdom) is also called on the mobile phone (React native) and browser; the final rendering object generated by ReactHostComponent is different, and we only discuss the browser environment at present. There is no suspense between strings and numbers, so let's not go into it in depth. Further, the rendering objects corresponding to the native dom nodes of html, such as div, are examples of ReactDOMComponent. How to generate a dom node from {type:'div',...} is in this class (mountComponent method). (Interested in how to generate div, span, input, select and other dom nodes, you can explore ReactDOMComponent. There is no specific discussion here. This article just wants to summarize the whole rendering process of React. Here's just one simplest example code:)

class ReactDOMComponent {
  constructor( vdom ) {
    this._currentElement = vdom;
  }
  mountComponent() {
    var result;
    var props = this._currentElement.props;
    if ( this._currentElement.type === 'div' ) {
      result = document.createElement( 'div' );
      
      for(var key in props ) {
        result.setAttribute( key, props[ key ] );
      }
    } else {
      // Other types
    }
    // Iterative subnodes
    props.children.forEach( child=>{
      var childRenderedComponent =  = instantiateReactComponent( child );
      var childMarkup = childRenderedComponent.mountComponent();
      result.appendChild( childMarkup );
    } )
    return result;
  }
}

Let's look at React Composite Component Wrapper, the rendering object of React component (mainly implemented in React Composite Component, React Composite Component Wrapper is just a wrapper to prevent circular references)

// The following is pseudocode
class ReactCompositeComponent {
  _currentElement: vdom,
  _rootNodeID: 0,
  _compositeType:
  _instance: 
  _hostParent:
  _hostContainerInfo: 
  // See ReactUpdateQueue
  _updateBatchNumber:
  _pendingElement:
  _pendingStateQueue:
  _pendingReplaceState:
  _pendingForceUpdate:
  _renderedNodeType:
  _renderedComponent:
  _context:
  _mountOrder:
  _topLevelWrapper:
  // See ReactUpdates and ReactUpdateQueue.
  _pendingCallbacks:
  // ComponentWillUnmount shall only be called once
  _calledComponentWillUnmount:

  // render to dom node
  mountComponent( transaction, hostParent, hostContainerInfo, context ) {
    // Initialization of React.Component
    var Component = this._currentElement.type;
    var publicProps = this._currentElement.props;
    /*
      React.Component There are two components:
      new Component(publicProps, publicContext, updateQueue);
      new StatelessComponent(Component);
      There are three corresponding compositeType s
      this._compositeType = StatelessFunctional | PureClass | ImpureClass,
      Component types and compositeType s are differentiated in source code, but for simplicity, only the code of one of the most commonly used components is illustrated here.
    */
    var inst = new Component(publicProps, publicContext, updateQueue);
    
    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = updateQueue;
    
    // Render Object Storage Component Object
    this._instance = inst;

    // Connect component objects with rendering objects through map
    ReactInstanceMap.set(inst, this);
    /*
      ReactInstanceMap: {
              -----------------------------------------------
              |                                              |
              v                                              |
        React.Component: ReactCompositeComponentWrapper {    |
          _instance:  <-------------------------------------
        }
      }
      In this way, both sides can get each other's references when they need each other.
    */

    // Generating dom of React.Component
    // Component lifecycle function componentWillMount is called
    inst.componentWillMount();
    // Call render method to return virtual dom of component
    var renderedElement = inst.render();
    // save nodeType  
    var nodeType = ReactNodeTypes.getType(renderedElement);
    this._renderedNodeType = nodeType;
    // Generating rendering objects based on virtual dom of components
    var child = instantiateReactComponent(renderedElement)
    this._renderedComponent = child;
    // Generating a true dom node
    // The real code in the source code should be var markup = ReactReconciler. mountComponent (child,...).
    // To simplify the explanation, let's not go into ReactReconciler.mountComponent and do something else.
    var markup = child.mountComponent(); 
    // The component life cycle function componentDidMount is registered in the callback function and triggered when the entire dom node tree is added to the container node.
    transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
    return markup;
  }
}
// static member
ReactCompositeComponentWrapper._instantiateReactComponent = instantiateReactComponent
  1. The final process is:

<div className="test">
  <span onClick={this.click}>CLICK ME</span>
  <Form/>
</div>

  |
babel and React.createElement
  |
  v

{
  type: 'div',
  props: {
    className: 'xxx',
    children: [ {
      type: 'span',
      props: {
        children:
      },
      ref:
      key:
    }, {
      type: Form,
      props: {
        children:
      },
      ref:
      key:
    } ] | Element
  }
  ref: 'xxx',
  key: 'xxx'
}

  |
var domNode = new ReactDOMComponent( vdom ).mountComponent();
  |
  v

domNode = {
  ReactDOMComponent -> <div/>
  props: {
    children: [
      ReactDOMComponent -> <span/>
      ReactCompositeComponentWrapper.render() -> vdom -> instantiateReactComponent(vdom) -> <form><input/></from>
    ]
  }
}

  |
  |
  v
containerDomNode.appendChild( domNode );

This is a basic process of React rendering dom. The next project summarizes the process of update dom, which is what happened after setState.

Keywords: Javascript React Mobile

Added by Truti on Sun, 30 Jun 2019 23:50:34 +0300