The content of this article is about the analysis of the first rendering of React (pure DOM elements). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
React is a very large library. Since ReactDom and ReactNative must be considered at the same time, as well as server rendering, etc., the code has a high degree of abstraction and a very deep nesting level. Reading its source code is very difficult. the process of. In the process of learning React source code, this series of articles helped me the most, so I decided to talk about my understanding based on this series of articles. This article will use a lot of examples from the original text. If you want to experience the original feeling, it is recommended to read the original text.
This series of articles will be based on React 15.4.2.
When writing React projects, we usually write directly in the form of JSX, and JSX is compiled by Babel Finally, the HTML tag will be converted into the function form of React.createElement. If you want to have a more in-depth understanding, you can read this article I wrote before: Virtual DOM you don’t know (1): Introduction to Virtual Dom. The h function in the article defaults to React.createElement if it is not configured in Babel.
Below, we will look at how React renders from the simplest example
ReactDOM.render( <h1>hello world</h1>, document.getElementById('root') );
After JSX compilation, it will look like this
ReactDOM.render( React.createElement( 'h1', { style: { "color": "blue" } }, 'hello world' ), document.getElementById('root') );
First come Take a look at the source code of React.createElement
.
// 文件位置:src/isomorphic/React.js var ReactElement = require('ReactElement'); ... var createElement = ReactElement.createElement; ... var React = { ... createElement: createElement, ... } module.exports = React;
The final implementation needs to be viewedReactElement.createElement
:
// 文件位置:src/isomorphic/classic/element/ReactElement.js ReactElement.createElement = function (type, config, children) { ... // 1. 将过滤后的有效的属性,从config拷贝到props if (config != null) { ... for (propName in config) { if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { props[propName] = config[propName]; } } } // 2. 将children以数组的形式拷贝到props.children属性 var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i <p>Essentially only does 3 things:</p><ol class=" list-paddingleft-2"> <li><p> Copy the filtered valid properties from config to props</p></li> <li><p>Copy children to the props.children property in the form of an array</p></li> <li><p>Default Property assignment </p></li> </ol><p>The final return value is <code>ReactElement</code>. Let's take a look at what it does</p><pre class="brush:php;toolbar:false">// 文件位置:src/isomorphic/classic/element/ReactElement.js var ReactElement = function (type, key, ref, self, source, owner, props) { var element = { // This tag allow us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }; ... return element; };
In the end it just returns a simple object. The call stack is like this:
React.createElement |=ReactElement.createElement(type, config, children) |-ReactElement(type,..., props)
We name the ReactElement generated here ReactElement[1]
, which will be passed as a parameter to ReactDom.render.
ReactDom.render will eventually call ReactMount's _renderSubtreeIntoContainer:
// 文件位置:src/renderers/dom/client/ReactMount.js _renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) { ... var nextWrappedElement = React.createElement( TopLevelWrapper, { child: nextElement } ); ... var component = ReactMount._renderNewRootComponent( nextWrappedElement, container, shouldReuseMarkup, nextContext )._renderedComponent.getPublicInstance(); ... return component; }, ... var TopLevelWrapper = function () { this.rootID = topLevelRootCounter++; }; TopLevelWrapper.prototype.isReactComponent = {}; TopLevelWrapper.prototype.render = function () { return this.props.child; }; TopLevelWrapper.isReactTopLevelWrapper = true; ... _renderNewRootComponent: function ( nextElement, container, shouldReuseMarkup, context ) { ... var componentInstance = instantiateReactComponent(nextElement, false); ... return componentInstance; },
Here it will be called again Another file instantiateReactComponent:
// 文件位置:src/renders/shared/stack/reconciler/instantiateReactComponent.js function instantiateReactComponent(node, shouldHaveDebugID) { var instance; ... instance = new ReactCompositeComponentWrapper(element); ... return instance; } // To avoid a cyclic dependency, we create the final class in this module var ReactCompositeComponentWrapper = function (element) { this.construct(element); }; Object.assign( ReactCompositeComponentWrapper.prototype, ReactCompositeComponent, { _instantiateReactComponent: instantiateReactComponent, } );
Here another file ReactCompositeComponent will be called:
// 文件位置:src/renders/shared/stack/reconciler/ReactCompositeComponent.js var ReactCompositeComponent = { construct: function (element) { this._currentElement = element; this._rootNodeID = 0; this._compositeType = null; this._instance = null; this._hostParent = null; this._hostContainerInfo = null; // See ReactUpdateQueue this._updateBatchNumber = null; this._pendingElement = null; this._pendingStateQueue = null; this._pendingReplaceState = false; this._pendingForceUpdate = false; this._renderedNodeType = null; this._renderedComponent = null; this._context = null; this._mountOrder = 0; this._topLevelWrapper = null; // See ReactUpdates and ReactUpdateQueue. this._pendingCallbacks = null; // ComponentWillUnmount shall only be called once this._calledComponentWillUnmount = false; if (__DEV__) { this._warnedAboutRefsInRender = false; } } ... }
We use ReactCompositeComponent[T]
to represent the top-level component generated here.
The entire call stack is like this:
ReactDOM.render |=ReactMount.render(nextElement, container, callback) |=ReactMount._renderSubtreeIntoContainer() |-ReactMount._renderNewRootComponent( nextWrappedElement, // scr:------------------> ReactElement[2] container, // scr:------------------> document.getElementById('root') shouldReuseMarkup, // scr: null from ReactDom.render() nextContext, // scr: emptyObject from ReactDom.render() ) |-instantiateReactComponent( node, // scr:------------------> ReactElement[2] shouldHaveDebugID /* false */ ) |-ReactCompositeComponentWrapper( element // scr:------------------> ReactElement[2] ); |=ReactCompositeComponent.construct(element)
The hierarchical structure between components is like this:
After the top-level component is built, the next step is to call batchedMountComponentIntoNode (_renderNewRootComponent method from ReactMount) to render the page.
The above is the detailed content of Analysis of React's first rendering (pure DOM element). For more information, please follow other related articles on the PHP Chinese website!