This is the 14th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Although there are more and more frameworks for JSX syntax, React is still the most relevant one. React and JSX were controversial when they first appeared in 2013, but now more and more people are saying “Wow!” Let’s get to know the JSX and talk about how JSX code is transformed into the DOM.

Consider a few questions before you begin:

  1. JSX What is the nature of JS, and what is the relationship between it and JS?
  2. Why use itJSX? What are the consequences of not using it?
  3. JSX What is the function module behind it, and what does this function module do?

If you can’t come up with a clear and systematic way of thinking about these issues, you’re probably oversimplifying JSX. Most people simply understand it as a template syntax, but in fact, JSX is one of the features of React framework, and there are numerous connections between React and its operation mechanism. The answers to the above three questions are precisely hidden in this “connection”.

The essence of JSX: Syntactic extensions to JavaScript

What exactly is JSX? Here’s a definition on React:

JSX is a syntactic extension of JavaScript that is close to the template language, but has full JavaScript capabilities.

The “syntax extension” part is almost unambiguous, but “it has full JavaScript capabilities” is always confusing. JSX and JS are not in the same league. This leads to the question of how JSX syntax works in JavaScript.

How JSX syntax works in JavaScript: Know Babel

Facebook describes JSX as an “extension” of JavaScript, not a “version” of JavaScript, which directly determines that browsers don’t support JSX the way JavaScript does naturally. So how does JSX syntax work in JavaScript? React has already given us a clue:

JSX will compile to react.createElement (), and react.createElement () will return a JS object called “React Element”.

When JSX is compiled, it becomes a call to react. createElement, so don’t worry about what the React.createElement API does. Let’s start with how this “compile” works: The “compile” is done by Babel.

What is Babel?

Babel is a toolchain for converting ECMAScript 2015+ version code into backwardly compatible JavaScript syntax so it can run in current and older versions of browsers or other environments. – the Babel’s official website

For example, the ES2015+ release introduced a new syntax called “template strings,” which is not compatible with some older browsers. Here is an example of template string code:

const name = "Guy Fieri";
const place = "Flavortown";
`Hello ${name}, ready for ${place}? `;
Copy the code

Babel can translate this code into ES5 code that most older browsers will recognize:

const name = "Guy Fieri";
const place = "Flavortown";
"Hello ".concat(name, ", ready for ").concat(place, "?");
Copy the code

Similarly, Babel has the ability to translate JSX syntax into JavaScript code.

So what exactly will Babel do with JSX? Why don’t we just open up Babel’s playground and take a look. The code is as follows:

<div className="App">
	<h1 className="title">I am the title</h1>
	<p className="content">I am the content</p>
</div>
Copy the code

After the treatment:

"use strict";

/*#__PURE__*/
React.createElement("div", {
  className: "App"
}, /*#__PURE__*/React.createElement("h1", {
  className: "title"
}, "I am the title"), /*#__PURE__*/React.createElement("p", {
  className: "content"
}, "I am the content"));
Copy the code

All JSX tags are converted to the React. CreateElement call, which means that JSX is actually written as React. CreateElement, which looks a bit like HTML, but only “looks like”. The essence of JSX is the react. createElement syntax for JavaScript calls, which perfectly echoes the React official statement that JSX has JavaScript capabilities.

React’s motivation for choosing JSX syntax

To put it another way, JSX is equivalent to a call to React. CreateElement. Why not create elements with React.

The reason is simple enough. Let’s look at the JSX code for a relatively complex component compared to the react.createElement call. Their respective shapes are shown below, with the JSX code on the left and the react.createElement call on the right:

On the premise of consistent function effect, JSX code has clear hierarchy and nested relationship. The React.createElement code, on the other hand, is a very confusing mix that is not only unfriendly to read but also hard to write.

JSX syntactic sugar allows front-end developers to create virtual DOM using htML-like tag syntax that we are most familiar with, reducing learning costs while improving r&d efficiency and experience.

How to map JSX to DOM: createElement source code

To get started, try reading the line-by-line parsing of the appended source code to get a rough idea of what each line of createElement does:

export const createElement = (type, config, children) = > {
  // The propName variable is used to store element attributes that need to be used later
  let propName;
  // props is the set of key-value pairs used to store element attributes
  const props = {};
  // Key, ref, self, source are all attributes of the React element
  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  The config object stores the attributes of the element
  if(config ! =null) {
    // The first thing you do is assign the ref, key, self, and source attributes in order
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    // String the key here
    if (hasValidKey(config)) {
      key = "" + config.key;
    }
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Move all the properties in config to props one by one
    for (propName in config) {
      if (
        // Filter out properties that can be pushed into the props object
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
    // childrenLength is the number of children of the current element; the 2 subtracted is the length of the type and config parameters
    const childrenLength = arguments.length - 2;
    // If type and config are omitted, there is only one argument left, which generally means that the text node is present
    if (childrenLength === 1) {
      // assign this parameter directly to props. Children
      props.children = children;
    } else if (childrenLength > 1) {
      // Declare an array of children
      const childArray = Array(childrenLength);
      // Push the child element into the array
      for (let i = 0; i < childrenLength; i++) {
        childArray[i] = arguments[i + 2];
      }
      // Finally, assign this array to props. Children
      props.children = childArray;
    }
    / / defaultProps processing
    if (type && type.defaultProps) {
      const defaultProps = type.defaultProps;
      for (propName in defaultProps) {
        if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}}// Finally returns a call to ReactElement execution method, passing in the arguments just processed
    returnReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props ); }};Copy the code

Input interpretation: What information is needed to create an element

Let’s take a look at the method input:

export function createElement(type, config, children)

CreateElement has three inputs that contain all the information React needs to know to create an element.

  • type: Indicates the type of a node. It can be a standard HTML tag string like “H1” or “div”, it can be a React component type orReact fragment type
  • config: is passed in as an object. All properties of the component are stored as key-value pairsconfig In the object
  • children: is passed in as an object, which records nested content between component tags, known as child nodes or child elements

If the text description seems abstract to you, the following call example can help you improve your understanding of the concept:

React.createElement("ul", {
  // Pass in attribute key-value pairs
  className: "list"
   // From the third entry, the arguments passed in are children
}, React.createElement("li", {
  key: "1"
}, "1"), React.createElement("li", {
  key: "2"
}, "2"));
Copy the code

The DOM structure is as follows:

<ul className="list">
    <li key="1">1</li>
    <li key="2">2</li>
</ul>
Copy the code

CreateElement function body disassembly

CreateElement’s logical task flow is the logical task flow for createElement. For this process, the following flow chart is summarized:

CreateElement doesn’t have a very complicated logic involving algorithms or the real DOM; every step of createElement is pretty much formatting the data.

To put it more bluntly, createElement acts as a “converter,” a data-processing layer, between the developer and the ReactElement call. It can take relatively simple parameters from the developer, format them as ReactElement expects, and create elements by calling ReactElement. The whole process is shown below:

As it turns out, createElement is simply a “parameter mediation.”

The first knowledge of the virtual DOM

CreateElement returns a call to ReactElement at the end. ReactElement = ReactElement = ReactElement = ReactElement

const ReactElement = function (type, key, ref, self, source, owner, props) {
  const element = {
    // REACT_ELEMENT_TYPE is a constant that identifies the object as a ReactElement
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in attribute assignment
    type: type,
    key: key,
    ref: ref,
    props: props,
    // Record the component that created the element
    _owner: owner,
  };
  //
  if (__DEV__) {
    // Here are some processing for __DEV__ environment. It is not meaningful for you to understand the main logic, so I omit it directly to avoid confusion
  }
  return element;
};
Copy the code

Logically, ReactElement does only one thing: “create” or, to be more precise, “assemble” : ReactElement “assembles” the passed argument into the Element object according to its specifications and returns it to the react. createElement, which ultimately passes it back to the developer. The whole process is shown below:

If you want to verify this, try printing the JSX section of the App component in the example:

const AppJSX = (<div className="App">
  <h1 className="title">I am the title</h1>
  <p className="content">I am the content</p>
</div>)
console.log(AppJSX)
Copy the code

You can see that it is indeed a standard ReactElement object instance, as shown in the output below:

This instance of the ReactElement object is essentially a description of the DOM in the form of a JavaScript object, known as the “virtual DOM” (to be precise, a node in the virtual DOM).

Since it’s a “virtual DOM,” that means there’s still some distance between the actual DOM rendered to the page, and that “distance” is filled in by the beloved reactdom.render method.

A call to the React. Render function is included in every React project entry file. Here’s a quick introduction to the reactdom.render method’s entry rules:

ReactDOM.render(
    // ReactElement to render
    element, 
    // The target container to which the element is mounted (a real DOM)
    container,
    // The optional callback function can be used to process the logic after rendering
    [callback]
)
Copy the code

The reactdom. render method takes three arguments, the second of which is an actual DOM node. The actual DOM node acts as the “container” into which the React element is eventually rendered. For example, the App component in the example would correspond to the render call like this:

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Copy the code

Note that the real DOM must exist. For example, in the index.html of the App component, the root node with id root has been preset in advance:

conclusion

  1. JSX is a syntactic extension of JavaScript that is close to the template language, but has full JavaScript capabilities. The essence of JSX is the react. createElement syntactic sugar for JavaScript calls

  2. JSX syntactic sugar allows front-end developers to create virtual DOM using htML-like tag syntax that we are most familiar with, reducing learning costs while improving r&d efficiency and experience.

  3. JSX is converted to ReactElement by reac. createElement. Render renders the ReactElement virtual node into a real node and mounts it to HTML.