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 domcreateElement
The 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 componentisReactComponent
Judgment 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.createElement
So 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.