I Love ReactJS

Analysis of React Source Code (1): implementation and mounting of components

when we are proficient in using React for front-end development, we will inevitably have a strong interest in the internal mechanism of React. What are the components? Is it the real DOM? What is the basis for the execution of lifecycle functions?

in this article, let's first study the implementation and mounting of React components.

1. What is the component

first write the simplest component:

after the above code is written, we get the & lt;A / & gt; component, so let's figure out what & lt;A / & gt; is. Print it out with console.log :

you can see that & lt;A / & gt; is actually a js object rather than a real DOM. Notice that props is an empty object at this time. Next, let's print & lt;A>

, which is component A & lt;/div> , and see what the console outputs:

We see that props has changed. Because div and div are nested in the & lt;A / & gt; component, the children attribute is added to the props describing the & lt;A / & gt; object, whose value is the js object describing div . By the same token, if we do multi-level component nesting, we are actually adding children fields and corresponding description values to the props of the parent object, that is, the multi-level nesting of js objects.

the above description is based on the React development model of ES6. In fact, the components created by the React.createClass ({}) method in ES5 are exactly the same as those in ES6, and can also be verified by printing the results of the components on the console. I will not repeat them here.

so how is the React component that looks like a HTML tag actually an object?

because our component declaration is based on React and Component , we first open React.js and see the following code:

when we import React from 'react' , we introduce the React object provided in the source code. When extends Component , the Component class is inherited. Two points need to be explained here:

  • Why can it be successfully introduced when module.exports is clearly used in the source code instead of export default ? This is actually the work of the babel parser. It makes (ES6) import = = (CommonJS) require . In typescript, a strict export default declaration is required, so import React from 'react' cannot be used under typescript. Interested readers can give it a try.
  • We can write extends Component or extends React.Component . Is there any difference between the two? The answer is no. Because Component is a reference to React.Component . That is to say, Component = = React.Component , you can write whatever you want in the actual project.

follow the clue of ReactComponent , we open node_modules/react/lib/ReactComponent.js :

the above code is the most familiar constructor, and you must be familiar with it. At the same time, we also note that setState is defined as a method with two parameters on the prototype, which we will explain in the chapter on the React update mechanism.

the above code shows that component A, which we declared at the beginning, is actually a subclass of the ReactComponent class, and its prototype has methods such as setState . In this way, component An already has the most basic prototype.

2. Initialization of components

After

declares A, we can customize methods internally, or we can use lifecycle methods, such as ComponentDidMount , which are exactly the same as when we write "classes". The only difference is that the component class must have render method output similar to & lt;div>, which is the structure of component A & lt;/div> and mounted on the real DOM in order to trigger the component's life cycle and become part of the DOM tree. First, let's look at how the "class" of ES6 initializes a react component.

put the original sample code into babel:

where _ Component is the object ReactComponent , and the _ inherit method is the functional implementation of the extends keyword. These are all ES6-related contents, which we will ignore for the time being. The point is that we find that the render method actually calls the React.createElement method (actually the ReactElement method). Then we open ReactElement.js :

when we see here, we find that virtually every component object is an object of type ReactElement created through the React.createElement method. In other words, ReactElment is an object that internally records the characteristics of the component and tells React what you want to see on the screen. In ReactElement :

parameters function
? typeof identification information of the component
key DOM structure identification to improve update performance
props substructure related information (add children field / not empty) and component properties (such as style )
ref reference to the real DOM
_ owner _ owner = = ReactCurrentOwner.current (ReactCurrentOwner.js), the value is the object that created the current component, and the default value is null.

after reading the above, I believe you already have some understanding of the essence of React components. The js object of type ReactElement created by executing React.createElement is the "React component", which corresponds exactly to the result printed by the console. To sum up, if we declare React components through the class keyword, they are js objects of type ReactElement until they are resolved to real DOM.

Summary

supplement the previous mind map:

3. Mounting of components

We know that custom components / native DOM/ strings can be mounted in the form of ReactDOM.render (component,mountNode) ,

so how is the mount process implemented?

ReactDOM.render actually calls the internal ReactMount.render , and then executes ReactMount._renderSubtreeIntoContainer . Literally, you can see the logic of inserting "sub-DOM" into the container. Let's take a look at the source code implementation:

this code is very important, and all the functions of the render function are here (click on the picture for a larger picture).

Let's first parse the parameters passed into _ renderSubtreeIntoContainer :

when rendering for the first time

parameters function
parentComponent the parent component of the current component, null
nextElement the component to be inserted into the DOM, such as helloWorld
container the container to be inserted, such as document.getElementById ('root')
callback callback function after completion

the functions of these parameters are easy to understand, so let's do a logical analysis line by line:

  • line 2: add the current component to the props attribute of the previous level. (it has been stated at the beginning of this article that the parent-child nesting relationship is provided by props )
  • line 4-22: call the getTopLevelWrapperInContainer method to determine whether a component exists under the current container, and mark it as prevComponent ; if so, the prevComponent is true , and the update process is executed, that is, the _ updateRootComponent method is called. If it does not exist, uninstall it. (call unmountComponentAtNode method)
  • line 24: whether it is updated or uninstalled, it will eventually be mounted on the real DOM. Look at the source code of . _ renderNewRootComponent :

analyze the flow:

The instantiateReactComponent wrapper method appears in line 3 of

    • , which we'll talk about later.

In line 5 of

  • , batchedMountComponentIntoNode calls mountComponentIntoNode in the form of a transaction (the transaction will be parsed with a special article). This method returns the HTML corresponding to the component, marked as the variable markup . And mountComponentIntoNode finally calls _ mountImageIntoNode . Take a look at the source code:

the core code is the last two lines. setInnerHTML is a method that sets markup to the innerHTML property of container , which completes the insertion of DOM. The precacheNode method is to store the processed component object in the cache to speed up the structure update.

the process of initializing and mounting React components is basically clear here. In the use of the ReactDOM.render () method, we will notice that this method can mount the React component, the string, or the native DOM. We now know that mounting makes use of the innerHTML attribute, but does React handle different element structures differently?

As we mentioned earlier, in the penultimate step of component mounting, which is the execution of the _ renderNewRootComponent method, we see a method called instantiateReactComponent that returns a processed object. Let's look at the source code of instantiateReactComponent :

the passed parameters node are the component parameters of the ReactDOM.render method. Input node and output instance can be summarized as follows:

node actual parameters result
null / false empty create ReactEmptyComponent component
object & & type = = string Virtual DOM create ReactDOMComponent component
object & & type! = string React components create ReactCompositeComponent component
string string create ReactTextComponent component
number numbers create ReactTextComponent component

sort out the process:

  • according to the different parameters passed in ReactDOM.render () , four types of wrapper components will be created inside the React, marked as componentInstance .
  • is then passed as a parameter in the mountComponentIntoNode method, from which the corresponding HTML of the component is obtained, which is recorded as the variable markup .
  • the DOM insertion is completed by setting the property innerHTML of the real DOM to markup .

so the question is, how do you parse HTML in the second step above? The answer is that in the first step of encapsulating into four types of components, the mountComponet method is given to the encapsulated component, and the execution of this method triggers the lifecycle of the component, thus parsing the HTML.

of course, the most commonly used of these four categories of components is the ReactCompositeComponent component, that is, the React component, which has a complete life cycle and is the most critical component feature of React. We will explain the detailed component types and lifecycles in the next article.

4. Summary

use a diagram to sort out the flow of React components from declaration to initialization to mount:

Exit mobile version