JSX and virtual DOM

Time:2021-1-17

JSX is a kind of syntax sugar, which is compiled by BabelReact.createElement(component, props, ...children)Function.

for example

const Element = (
  <div className="title">
    Hello 
    <span style={{fontSize:"20px",color:"#f00"}}>World!</span>
  </div>
)

After Babel compilation:

const Element = React.createElement(
  'div',
  {
    className: 'title',
  },
  'Hello',
  React.createElement(
    'span',
    {
      style: {
        fontSize: '20px',
        color: '#f00',
      },
    },
    'World!'
  )
);

JSX syntax is compiled by Babel to generate an object, namely virtual dom. In react, the virtual DOM is rendered as a real Dom and bound to the corresponding node through the render function

import React from 'react'
import ReactDOM from 'react-dom'

//Virtual DOM
const Element = (
  <div className="title">
    Hello 
    <span style={{fontSize:"20px",color:"#f00"}}>World!</span>
  </div>
)
/*
Equivalent to
const Element = React.createElement(
  'div',
  {
    className: 'title',
  },
  'Hello',
  React.createElement(
    'span',
    {
      style: {
        fontSize: '20px',
        color: '#f00',
      },
    },
    'World!'
  )
)
*/

//Binding node
const el = document.getElementById('root')

//Render method render to page
ReactDOM.render(Element, el)

Here you need two functions to render the page:createElementandRender method

CreateElement function

The createElement function simplifies the parameters after Babel transformation and returns the object type and props

/**
 *Generating virtual DOM objects
 *@ param {string} type DOM node type
 *@ param {object} config attribute object
 *@ param {*} [children] subarray
 *
 * @return {object} {type,props}
 */
function createElement(type,config,children) {
  let props = {};
  for(let propsName in config){
    props[propsName] = config[propsName]
  };

  let childsLen = arguments.length - 2;
  if(childsLen===1){
    props.children = [children]
  }else if(childsLen>1){
    props.children = Array.prototype.slice.call(arguments, 2)
  }

  return { type, props }

}

Render function

The render function is responsible for transforming the virtual DOM into a real domcreateElementThe generated virtual DOM object is passed into the first parameter of the render function to construct the type value and props object

/**
 *@ param {createElement} element virtual DOM object
 *DOM node bound to @ param {HtmlElement} container
 */
function render(element, container) {
  if (typeof element === 'string') {
    return container.appendChild(document.createTextNode(element))
  }

  let type  = element.type;
  let props  = element.props;

  if ( type.isReactComponent ){// if it is a class component
    element = new type(props).render()
    type = element.type
    props = element.props
  }Else if (type of type ='function ') {// if it is a function component
    element = type(props)
    type = element.type
    props = element.props
  }

  const el = document.createElement(type)
  for (let propName in props) {
    let value = props[propName]
    if (propName === 'className') {
      el.className = value
    } else if (propName === 'style') {
      let cssText = Object.keys(value).map((attr) => {
          let _attr = attr.replace(/([A-Z])/g, (a) => `-${a.toLocaleLowerCase()}`);
          return `${_attr}:${value[attr]}`
        }).join(';');

      el.style.cssText = cssText
    } else if (propName === 'children') {
      value.forEach((item) => render(item, el))
    } else {
      el.setAttribute(propName, value)
    }
  }
  return container.appendChild(el)
}

Here we also need to determine whether the type value is a class component or a function component

Class component

As for how to judge class components, we can add static properties to the component class inherited by the componentisReactComponentJudgment for render function

class Component {
  static isReactComponent = true;
  constructor(props){
    this.props = props;
  }
}

Class component

let Element = React.createElement(fnElemen, { name: 'Hello', fontSize: '28px' })

class clsComp extends React.Component {
  render() {
    return React.createElement(
      'h1',
      { className: 'title' },
      this.props.name,
      React.createElement(
        'span',
        {
          style: { color: '#0f0', fontSize: this.props.fontSize }
        },
        'World'
      )
    )
  }
};

let Element = React.createElement(clsComp, { name: 'Hello', fontSize: '28px' })

ReactDOM.render(Element, document.getElementById('root'))

Function component

function fnElemen(props){
  return React.createElement(
    'h1',
    { className: 'title' },
    props.name,
    React.createElement(
      'span',
      { style: { color: '#0f0', fontSize: props.fontSize } },
      'World'
    )
  )
}

let Element = React.createElement(fnElemen, { name: 'Hello', fontSize: '28px' })

ReactDOM.render(Element, document.getElementById('root'))

Common problems in development

Why must react be quoted

React must be introduced into the scope, otherwise the compilation will fail, because JSX will be compiled asReact.createElementSo react must be introduced in the scope of JSX.

User defined components start with capital letters

Babel will judge the elements beginning with lowercase letters as native DOM tags when compiling,createElementWill compile it into a string, such as<div>perhaps<span>So when you write a component, you need to start with a capital lettercreateElementTo compile the first variable as an object

import React from 'react';

//Wrong! Components should start with a capital letter:
function hello(props) {
  //Right! This use of < div > is legal because div is a valid HTML tag
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  //Wrong! React thinks < hello / > is an HTML tag because it doesn't start with a capital letter:
  return <hello toWhat="World" />;
}

The default value of props is “true”

If you don’t assign a value to prop, its default value is true. The following two JSX expressions are equivalent:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

In general, we don’t recommend not passing value to prop, because it may be confused with ES6 object shorthand,{foo}yes{foo: foo}It’s short for, not for{foo: true}. This implementation is just to keep the behavior consistent with the tag attributes in HTML.

Recommended Today

DK7 switch’s support for string

Before JDK7, switch can only support byte, short, char, int or their corresponding encapsulation classes and enum types. After JDK7, switch supports string type. In the switch statement, the value of the expression cannot be null, otherwise NullPointerException will be thrown at runtime. Null cannot be used in the case clause, otherwise compilation errors will […]