What happened when we first called reactdom.render in the React project? Today I’ll trace the issue from a source perspective (focusing on the flow, not the details).

ReactDOMStackEntry

First we can find the Render method in the ReactDOM entry file, reactdomStackEntry.js. As you can see, the Render method is provided by the ReactMount component.

var ReactDOMStack = {
  findDOMNode: findDOMNode,
  render: ReactMount.render,
  unmountComponentAtNode: ReactMount.unmountComponentAtNode,
  version: ReactVersion,
  /* eslint-disable camelcase */
  unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
  unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer,
  /* eslint-enable camelcase */
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    // For TapEventPlugin which is popular in open source
    EventPluginHub: require('EventPluginHub'),
    // Used by test-utils
    EventPluginRegistry: require('EventPluginRegistry'),
    EventPropagators: require('EventPropagators'),
    ReactControlledComponent: require('ReactControlledComponent'),
    ReactDOMComponentTree,
    ReactDOMEventListener: require('ReactDOMEventListener'),
    ReactUpdates: ReactUpdates,
  },
};Copy the code

ReactMount.render

render: function(nextElement, container, callback) {
  return ReactMount._renderSubtreeIntoContainer(
    null,
    nextElement,
    container,
    callback,
  );
},Copy the code

And transferred to _renderSubtreeIntoContainer method, this method is the core content is as follows:

ReactMount._renderSubtreeIntoContainer

_renderSubtreeIntoContainer: function( parentComponent, nextElement, container, callback, ) { callback = callback === undefined ? null : callback; if (! React.isValidElement(nextElement)) { ... Var nextWrappedElement = react. createElement(TopLevelWrapper, {child: nextElement,}); var nextContext = getContextForSubtree(parentComponent); / / to get at the top of the current container components var prevComponent = getTopLevelWrapperInContainer (container); PrevComponent null if (prevComponent) {var prevWrappedElement = prevComponent._currentelement; var prevElement = prevWrappedElement.props.child; if (shouldUpdateReactComponent(prevElement, nextElement)) { var publicInst = prevComponent._renderedComponent.getPublicInstance(); var updatedCallback = callback && function() { validateCallback(callback); callback.call(publicInst); }; ReactMount._updateRootComponent( prevComponent, nextWrappedElement, nextContext, container, updatedCallback, ); return publicInst; } else { ReactMount.unmountComponentAtNode(container); } } var reactRootElement = getReactRootElementInContainer(container); var containerHasReactMarkup = reactRootElement && !! internalGetID(reactRootElement); var containerHasNonRootReactChild = hasNonRootReactChild(container); var shouldReuseMarkup = containerHasReactMarkup && ! prevComponent && ! containerHasNonRootReactChild; var component = ReactMount._renderNewRootComponent( nextWrappedElement, container, shouldReuseMarkup, nextContext, callback, )._renderedComponent.getPublicInstance(); return component; },Copy the code

Here are a few methods:

  • getTopLevelWrapperInContainer
  • shouldUpdateReactComponent
  • _renderNewRootComponent

getTopLevelWrapperInContainer

This method is used to retrieve the existing top-level container component, as shown below.

function getTopLevelWrapperInContainer(container) { var root = getHostRootInstanceInContainer(container); return root ? root._hostContainerInfo._topLevelWrapper : null; } function getHostRootInstanceInContainer(container) { var rootEl = getReactRootElementInContainer(container); var prevHostInstance = rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl); return prevHostInstance && ! prevHostInstance._hostParent ? prevHostInstance : null; }Copy the code

Call the two methods, getReactRootElementInContainer and ReactDOMComponentTree getInstanceFromNode.

getReactRootElementInContainer

function getReactRootElementInContainer(container) {
  if (!container) {
    return null;
  }
  if (container.nodeType === DOCUMENT_NODE) {
    return container.documentElement;
  } else {
    return container.firstChild;
  }
}Copy the code

ReactDOMComponentTree

This module has three methods, which are:

  • precacheChildNodesStore the React instance on the DOM node
  • getNodeFromInstanceGet the corresponding DOM node from an instance
  • getInstanceFromNodeGet the corresponding instance from a DOM node

shouldUpdateReactComponent

Determine whether the component needs to be updated.

function shouldUpdateReactComponent(prevElement, nextElement) { var prevEmpty = prevElement === null || prevElement === false; var nextEmpty = nextElement === null || nextElement === false; if (prevEmpty || nextEmpty) { return prevEmpty === nextEmpty; } var prevType = typeof prevElement; var nextType = typeof nextElement; if (prevType === 'string' || prevType === 'number') { return nextType === 'string' || nextType === 'number'; } else { return ( nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key ); }}Copy the code

You can see that the logic looks like this:

  • Both elements are zeronullreturntrue
  • If it istextComponent, then directly update
  • Otherwise, if the element is a DOM element or React element, and the type and key are the sametrue, the implementation of the update

ReactMount._renderNewRootComponent

This method is the core of _renderSubtreeIntoContainer, used to mount a new component to the DOM.

_renderNewRootComponent: function( nextElement, container, shouldReuseMarkup, context, callback, Through instantiateReactComponent) {/ / get the React Component Component instance var componentInstance = instantiateReactComponent(nextElement, false); if (callback) { componentInstance._pendingCallbacks = [ function() { validateCallback(callback); callback.call( componentInstance._renderedComponent.getPublicInstance(), ); }, ]; } // The initial render is synchronous but any updates that happen during // rendering, in componentWillMount or componentDidMount, will be batched // according to the current batching strategy. ReactUpdates.batchedUpdates( batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context, ); var wrapperID = componentInstance._instance.rootID; instancesByReactRootID[wrapperID] = componentInstance; return componentInstance; },Copy the code

instantiateReactComponent

Generate different React Components based on the parameters passed in.

if (node === null || node === false) { instance = ReactEmptyComponent.create(instantiateReactComponent); } else if (typeof node === 'object') { var element = node; var type = element.type; if (typeof type ! == 'function' && typeof type ! == 'string') { ... } // Special case string values if (typeof element.type === 'string') { instance = ReactHostComponent.createInternalComponent(element); } else if (isInternalComponentType(element.type)) { // This is temporarily available for custom components that are not string // representations. I.e. ART. Once those are updated to use the string // representation, we can drop this code path. instance = new element.type(element); // We renamed this. Allow the old name for compat. :( if (! instance.getHostNode) { instance.getHostNode = instance.getNativeNode; } } else { instance = new ReactCompositeComponentWrapper(element); } } else if (typeof node === 'string' || typeof node === 'number') { instance = ReactHostComponent.createInstanceForText(node); } else { invariant(false, 'Encountered invalid React node of type %s', typeof node); }Copy the code

There are three methods to generate three React component instances, depending on the element. Type:

  • ReactHostComponent.createInternalComponent(element)
  • new ReactCompositeComponentWrapper(element)
  • ReactHostComponent.createInstanceForText(node);

If isInternalComponentType(element.type) is set, instance = new Element.type (element); if isInternalComponentType(element.type) is set, instance = new Element.type (element); This code is ignored by me because it is the solution when React encapsulates internal components that are not expressed by strings, so we don’t have to worry about it. Let’s look at the three methods above, two of which call the ReactHostComponent module.

ReactHostComponent

Core code:

var ReactHostComponentInjection = { // This accepts a class that receives the tag string. This is a catch all // that can render any kind of tag. injectGenericComponentClass: function(componentClass) { genericComponentClass = componentClass; }, // This accepts a text component class that takes the text string to be // rendered as props. injectTextComponentClass: function(componentClass) { textComponentClass = componentClass; }}; function createInternalComponent(element) { invariant( genericComponentClass, 'There is no registered component for the tag %s', element.type, ); return new genericComponentClass(element); } /** * @param {ReactText} text * @return {ReactComponent} */ function createInstanceForText(text) { return new textComponentClass(text); }Copy the code

Two methods are provided to create components, and the implementation of two component classes is injected from other modules. After a search, found to be in ReactDOMStackInjection. Injected in js, we look at the code:

var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactComponentBrowserEnvironment = require('ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('ReactDOMComponent');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
var ReactDOMTextComponent = require('ReactDOMTextComponent');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactHostComponent = require('ReactHostComponent');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
var ReactUpdates = require('ReactUpdates');
var findDOMNode = require('findDOMNode');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
ReactGenericBatching.injection.injectStackBatchedUpdates(
  ReactUpdates.batchedUpdates,
);
ReactHostComponent.injection.injectGenericComponentClass(ReactDOMComponent);
ReactHostComponent.injection.injectTextComponentClass(ReactDOMTextComponent);
ReactEmptyComponent.injection.injectEmptyComponentFactory(function(
  instantiate,
) {
  return new ReactDOMEmptyComponent(instantiate);
});
ReactUpdates.injection.injectReconcileTransaction(ReactReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
ReactComponentEnvironment.injection.injectEnvironment(
  ReactComponentBrowserEnvironment,
);
findDOMNode._injectStack(function(inst) {
  inst = getHostComponentFromComposite(inst);
  return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
});Copy the code

You can see, this module by ReactHostComponent. Injection injected ReactDOMComponent and ReactDOMTextComponent. Some other modules are also injected, which we’ll use later.

Here the ReactDOMComponent and ReactDOMTextComponent are the real DOM tag generating modules. They are too much content, but the code is relatively simple and I won’t go into details here. Calling the mountComponent method of both modules generates DOM Markup. The difference is that the ReactDOMComponent will have the Markup of the following structure

{
  node: node,
  children: [],
  html: null,
  text: null,
  toString
}Copy the code

The ReactDOMTextComponent directly generates the String text to render in the DOM.

ReactCompositeComponent

The last class of components should be ReactCompositeComponentWrapper, but see instantiateReactComponent inside the code:

var ReactCompositeComponentWrapper = function(element) {
  this.construct(element);
};
Object.assign(
  ReactCompositeComponentWrapper.prototype,
  ReactCompositeComponent,
  {
    _instantiateReactComponent: instantiateReactComponent,
  },
);Copy the code

When this.construct is called, it also calls the ReactCompositeComponent, which is the user-defined component.

ReactUpdates.batchedUpdates

function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}Copy the code

ReactUpdates call batchedUpdate with batchingStrategy. The batchingStrategy is also injected with the ReactDOMStackInjection described earlier.

ReactUpdates.injection.injectReconcileTransaction(ReactReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);Copy the code

ReactUpdates were injected into the two modules, respectively is ReactReconcileTransaction and ReactDefaultBatchingStrategy.

To look at the first ReactDefaultBatchingStrategy, behind ReactReconcileTransaction met again, take a look at its code:

var ReactUpdates = require('ReactUpdates'); var Transaction = require('Transaction'); var emptyFunction = require('fbjs/lib/emptyFunction'); var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }}; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }}); var transaction = new ReactDefaultBatchingStrategyTransaction(); var ReactDefaultBatchingStrategy = { isBatchingUpdates: false, /** * Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ batchedUpdates: function(callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) {// If current updates were completed, Callback callback(a, b, c, d, e); Perform (callback, null, a, b, c, d, e); }}};Copy the code

. You can see our previous call ReactUpdates batchUpdates batchedUpdates actually calls are here, the inside of the logic is simple.

There is a transaction method that is used. That is, if an update is currently in progress, the callback is called transactionally.

transaction

Transaction is used extensively in the React source code to call a method transactionally.

Wrap a method with one or more Wrappers and execute it before and after the method call. The transaction ensures that the Initialize and close methods of the Wrapper will execute, regardless of whether the intended method succeeds or fails.

* <pre>
*                       wrappers (injected at creation time)
*                                      +        +
*                                      |        |
*                    +-----------------|--------|--------------+
*                    |                 v        |              |
*                    |      +---------------+   |              |
*                    |   +--|    wrapper1   |---|----+         |
*                    |   |  +---------------+   v    |         |
*                    |   |          +-------------+  |         |
*                    |   |     +----|   wrapper2  |--------+   |
*                    |   |     |    +-------------+  |     |   |
*                    |   |     |                     |     |   |
*                    |   v     v                     v     v   | wrapper
*                    | +---+ +---+   +---------+   +---+ +---+ | invariants
* perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
*                    | |   | |   |   |         |   |   | |   | |
*                    | |   | |   |   |         |   |   | |   | |
*                    | |   | |   |   |         |   |   | |   | |
*                    | +---+ +---+   +---------+   +---+ +---+ |
*                    |  initialize                    close    |
*                    +-----------------------------------------+
* </pre>Copy the code

Here we go back to just ReactDefaultBatchingStrategy, there are two wrapper code.

var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }}; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), };Copy the code

The initialize method is an empty function, and the close method is:

  1. Set the current update status to false
  2. flushBatchedUpdatesThis method is a bit more complicated and won’t be covered here. The main thing is to ensure that all components are updated correctly (flushBatchedUpdates->ReactUpdates.runBatchedUpdates->ReactCompositeComponent.performUpdateIfNecessary)

Now back to the code above:

ReactUpdates.batchedUpdates(
  batchedMountComponentIntoNode,
  componentInstance,
  container,
  shouldReuseMarkup,
  context,
);Copy the code

Can know, this is called the batchedMountComponentIntoNode for subsequent work.

function batchedMountComponentIntoNode( componentInstance, container, shouldReuseMarkup, context, ) { var transaction = ReactUpdates.ReactReconcileTransaction.getPooled( /* useCreateElement */ ! shouldReuseMarkup, ); transaction.perform( mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context, ); ReactUpdates.ReactReconcileTransaction.release(transaction); }Copy the code

Here the first assignment will use the top into the inside of the ReactUpdates another module ReactReconcileTransaction React is scheduling transaction module.

var SELECTION_RESTORATION = { initialize: ReactInputSelection.getSelectionInformation, close: ReactInputSelection.restoreSelection, }; /** * Suppresses events (blur/focus) that could be inadvertently dispatched due to * high level DOM manipulations (like temporarily removing a text input from the * DOM). */ var EVENT_SUPPRESSION = { initialize: function() { var currentlyEnabled = ReactBrowserEventEmitter.isEnabled(); ReactBrowserEventEmitter.setEnabled(false); return currentlyEnabled; }, close: function(previouslyEnabled) { ReactBrowserEventEmitter.setEnabled(previouslyEnabled); }}; /** * Provides a queue for collecting `componentDidMount` and * `componentDidUpdate` callbacks during the transaction. */ var ON_DOM_READY_QUEUEING = { initialize: function() { this.reactMountReady.reset(); }, close: function() { this.reactMountReady.notifyAll(); }}; . var TRANSACTION_WRAPPERS = [ SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING, ]; function ReactReconcileTransaction(useCreateElement) { this.reinitializeTransaction(); this.renderToStaticMarkup = false; this.reactMountReady = CallbackQueue.getPooled(); this.useCreateElement = useCreateElement; }... var Mixin = { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; },... } Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin); PooledClass.addPoolingTo(ReactReconcileTransaction);Copy the code

This is also done transactionally, with three wrappers:

  • Selection Restoration minimizes user Selection range during updates
  • Event Suppression Suppression of unnecessary Event distribution, such as blur events caused by the temporary deletion of an input element
  • On DOM Ready Queueing is provided during transaction executioncomponentDidMountcomponentDidUpdateA queue of callback functions

The getPooled method, which follows, is a way to use instance pools to avoid unnecessary GC.

Then use this transaction to call mountComponentIntoNode. Take a look at this method in detail.

mountComponentIntoNode

function mountComponentIntoNode(
  wrapperInstance,
  container,
  transaction,
  shouldReuseMarkup,
  context,
) {
  var markup = ReactReconciler.mountComponent(
    wrapperInstance,
    transaction,
    null,
    ReactDOMContainerInfo(wrapperInstance, container),
    context,
    0 /* parentDebugID */,
  );
  wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
  ReactMount._mountImageIntoNode(
    markup,
    container,
    wrapperInstance,
    shouldReuseMarkup,
    transaction,
  );
}Copy the code

Here you see operations on the key markup variable, which is the object we will eventually render into the DOM. Through ReactReconciler. MountComponent method to get the markup. ReactReconciler. MountComponent source code is as follows:

mountComponent: function( internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID, // 0 in production and for roots ) { var markup = internalInstance.mountComponent( transaction, hostParent, hostContainerInfo, context, parentDebugID, ); if ( internalInstance._currentElement && internalInstance._currentElement.ref ! = null ) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); } return markup; },Copy the code

It calls the internalInstance mountComponent, internalInstance here is actually said is obtained by instantiateReactComponent React in front of the Component instance.

// _renderNewRootComponent
var componentInstance = instantiateReactComponent(nextElement, false)Copy the code

Here nextElement is the React root element to render.

// _renderSubtreeIntoContainer
    var nextWrappedElement = React.createElement(TopLevelWrapper, {
      child: nextElement,
    });Copy the code

The implementation of TopLevelWrapper needs to pay attention to its render method.

var TopLevelWrapper = function() {
  this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
TopLevelWrapper.prototype.render = function() {
  return this.props.child;
};
TopLevelWrapper.isReactTopLevelWrapper = true;Copy the code

This.props. Child = nextElement (
, document.getelementByid (‘root’)); The inside of the < App / >.

The inside of the back ReactReconciler. MountComponent internalInstance mountComponent. Return through said instantiateReactComponent we know in front of three components:

  1. ReactDOMComponent
  2. ReactDOMTextComponent
  3. ReactCompositeComponent

The first two classes are simple. They are elements of the DOM itself, and eventually render their corresponding Markup. ReactCompositeComponent is more complex, so let’s just look at the key code:

//ReactCompositeComponent.mountComponent var Component = this._currentElement.type; var updateQueue = transaction.getUpdateQueue(); // Initialize the public class var doConstruct = shouldConstruct(Component); var inst = this._constructComponent( doConstruct, publicProps, publicContext, updateQueue, ); var renderedElement; if (! doConstruct && (inst == null || inst.render == null)) { renderedElement = inst; inst = new StatelessComponent(Component); this._compositeType = ReactCompositeComponentTypes.StatelessFunctional; } else {. . } markup = this.performInitialMount( renderedElement, hostParent, hostContainerInfo, transaction, context, );Copy the code

First get inst, get inst the call stack is this: enclosing _constructComponent – > enclosing _constructComponentWithoutOwner

// this._constructComponentWithoutOwner ... var Component = this._currentElement.type; if (doConstruct) { if (__DEV__) { return measureLifeCyclePerf( () => new Component(publicProps, publicContext, updateQueue), this._debugID, 'ctor', ); } else { return new Component(publicProps, publicContext, updateQueue); }}...Copy the code

CurrentElement. Type is the class inherited from react.component.component.component.function. RenderElement is then declared, with renderElement = inst for stateless (pure render component declared by function) components, undefined otherwise.

And then performInitialMount,

if (renderedElement === undefined) { renderedElement = this._renderValidatedComponent(); } var nodeType = ReactNodeTypes.getType(renderedElement); this._renderedNodeType = nodeType; var child = this._instantiateReactComponent( renderedElement, nodeType ! == ReactNodeTypes.EMPTY /* shouldHaveDebugID */, ); this._renderedComponent = child; var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID, );Copy the code

For non-Stateless components, renderedElement needs to be assigned. The call stack is: the enclosing _renderValidatedComponent – > enclosing _renderValidatedComponentWithoutOwnerOrContext

_renderValidatedComponentWithoutOwnerOrContext: function() {
  var inst = this._instance;
  var renderedElement;
  if (__DEV__) {
    renderedElement = measureLifeCyclePerf(
      () => inst.render(),
      this._debugID,
      'render',
    );
  } else {
    renderedElement = inst.render();
  }
  if (__DEV__) {
    // We allow auto-mocks to proceed as if they're returning null.
    if (renderedElement === undefined && inst.render._isMockFunction) {
      // This is probably bad practice. Consider warning here and
      // deprecating this convenience.
      renderedElement = null;
    }
  }
  return renderedElement;
},Copy the code

We end up with renderedElement after Inst.render ().

Then go down the child = this. _instantiateReactComponent (renderedElement) and markup = ReactReconciler. MountComponent (child,…). .

Can relate to, and here is the cycle of recursive calls ReactReconciler. MountComponent, know the child is not ReactCompositeComponent so far, The data structure for the final MarkUp. MarkUp can be found in DOMLazyTree:

// DOMLazyTree
{
  node: node,
  children: [],
  html: null,
  text: null,
  toString,
}Copy the code

Once you have the MarkUp, the final step is to mount the MarkUp into the actual DOM via reactmount._mountimageintonode.

ReactMount._mountImageIntoNode

_mountImageIntoNode: function( markup, container, instance, shouldReuseMarkup, transaction, ) { invariant( isValidContainer(container), 'mountComponentIntoNode(...) : Target container is not valid.', ); If (shouldReuseMarkup) {if (shouldReuseMarkup) {if (shouldReuseMarkup) { } // First render, transaction.useCreateElement = true if (transaction.useCreateElement) { while (container.lastChild) { container.removeChild(container.lastChild); } DOMLazyTree.insertTreeBefore(container, markup, null); } else { setInnerHTML(container, markup); ReactDOMComponentTree.precacheNode(instance, container.firstChild); }},Copy the code

The logic is simple. There are two ways to render markup into the DOM:

  • Empty the given container component, and then insert the markup into the given container
  • callsetInnerHTMLTo insert the markup into the given container and cache the virtual DOM to the actual DOM node

For the initial rendering, the first method is performed, emptying the container components and mounting the markup into the actual DOM. The call stack: DOMLazyTree insertTreeBefore – > insertTreeChildren

function insertTreeChildren(tree) { if (! enableLazy) { return; } var node = tree.node; var children = tree.children; if (children.length) { for (var i = 0; i < children.length; i++) { insertTreeBefore(node, children[i], null); } } else if (tree.html ! = null) { setInnerHTML(node, tree.html); } else if (tree.text ! = null) { setTextContent(node, tree.text); }}Copy the code

Mount all child components recursively into the DOM.

conclusion

The React and ReactDOM code is very abstract, which makes the code very complicated to read. This article is also limited to the overall process and does not delve into details because there are too many details.

Finally, a reactdom. render post-execution process diagram is sorted out, which can help to understand its whole process to a certain extent: the diagram is very large, and it is not clear even after clicking to enlarge it. It is suggested to save it and browse it locally, it will be clearer.

ReactDOM.render

Related articles