The React. The createElement method syntactic sugar
JSX is a syntactic extension of JavaScript that can be used for UI presentation:
const element = <h1>Hello, world!</h1>;
Copy the code
We typically use JSX in the render method of the component for layout and event binding:
class Home extends Component {
render() {
return (
<div onClick={()= > console.log('hello')}>
<h1>Hello, world!</h1>
<Blog title="deepred" />
</div>); }}Copy the code
One of the core mechanisms of React is the ability to create virtual DOM elements, which can be used to reduce manipulation of the real DOM to improve performance. JSX is the syntactic sugar for the virtual DOM
In our usual component writing, we usually write:
import React, { Component } from 'react';
class Demo extends Component {
render() {
return (
<h1>Hello, world!</h1>)}}Copy the code
React is not used in the code, so why introduce this variable?
Because JSX is the syntactic sugar for the react. createElement method:
const element = <h1 id="container" className="home">Hello</h1>;
/ / equivalent to the
const element = React.createElement("h1", {
id: "container".className: "home"
}, "Hello");
Copy the code
Babeljs. IO is recommended to see JSX compiled in action
React.createElement takes three arguments:
React.createElement(
type, // DOM types, such as div, h1
[props], // DOM attributes, such as id, class, event
[...children] // Child node, string, or an object generated by react. createElement
)
Copy the code
JSX replaces the tedious react. createElement pure JS method with an HTMl-like syntax, and the @babel/preset- React plugin is the key step: Responsible for changing all JSX files to React. CreateElement when webpack builds:
class Home extends Component {
render() {
return (
<div onClick={()= > console.log('hello')}>
<h1>Hello, world!</h1>
<Blog title="deepred" />
</div>); }}Copy the code
The compiled:
class Home extends Component {
render() {
return React.createElement("div", {
onClick: (a)= > console.log('hello')
}, React.createElement("h1".null."Hello, world!"), React.createElement(Blog, {
title: "deepred"})); }}Copy the code
In development, we rarely need to use the createElement method with JSX, but if we need to implement a component like this:
// Based on the type attribute passed in, Render the corresponding HTML element <Tag type="h1" id="hello" onClick={() => console.log('hello')}>this is a h1</Tag> <Tag type="p">this is a p</Tag>Copy the code
It is not possible to determine the corresponding tag according to the attribute of type, if else:
function Tag(props) {
const{ type, ... other } = props;if (type === 'h1') {
return <h1 {. other} >{props.children}</h1>
}
if (type === 'p') {
return <p {. other} >{props.children}</p>}}Copy the code
In this case, we need to use the underlying API:
function Tag(props) {
const{ type, ... other } = props;return React.createElement(type, other, props.children);
}
Copy the code
Implement a JSX renderer yourself
The virtual DOM is essentially a JS object:
const vnode = {
tag: 'div'.attrs: {
className: 'container'
},
children: [{tag: 'img'.attrs: {
src: '1.png'
},
children: []}, {tag: 'h3'.attrs: {},
children: ['hello']]}}Copy the code
Can tell @babel/preset-react to use h method name instead of JSX by adding /** @jsx h */ at the top of each file (the default is react.createElement)
/** @jsx h */
const element = <h1 id="container" className="home">Hello</h1>;
Copy the code
/** @jsx h */
const element = h("h1", {
id: "container".className: "home"
}, "Hello");
Copy the code
Now let’s start creating our own H function!
function h(nodeName, attributes, ... args) {
// Concat is used for flattening args because the elements in args arrays may also be arrays
/ / h (' div ', {}, [1, 2, 3]) h (' d ', {}, 1, 2, 3) are legitimate calls
constchildren = args.length ? [].concat(... args) :null;
return { nodeName, attributes, children };
}
Copy the code
const vnode = h("div", {
id: "urusai"
}, "Hello!");
/ / return
/ / {
// "nodeName": "div",
// "attributes": {
// "id": "urusai"
/ /},
// "children": [
// "Hello!"
/ /]
// }
Copy the code
H returns a vNode. With a vnode, we also need to convert the vNode into a real DOM:
function render(vnode) {
if (typeof vnode === 'string') {
// Generate a text node
return document.createTextNode(vnode);
}
// Generate element nodes and set attributes
const node = document.createElement(vnode.nodeName);
const attributes = vnode.attributes || {};
Object.keys(attributes).forEach(key= > node.setAttribute(key, attributes[key]));
if (vnode.children) {
// Recursively call render to generate child nodes
vnode.children.forEach(child= > node.appendChild(render(child)));
}
return node;
}
Copy the code
Now let’s use these two methods:
/** @jsx h */
const vnode = <div id="urusai">Hello!</div>;
const node = render(vnode);
document.body.appendChild(node);
Copy the code
After compiling and transcoding:
/** @jsx h */
const vnode = h("div", {
id: "urusai"
}, "Hello!");
const node = render(vnode);
document.body.appendChild(node);
Copy the code
We can also iterate over groups of numbers:
/** @jsx h */
const items = ['baga'.'hentai'.'urusai'];
const vnode = <ul>{items.map((item, index) => <li key={index}>{item}</li>)}</ul>;
const list = render(vnode);
document.body.appendChild(list);
Copy the code
After compiling and transcoding:
/** @jsx h */
const items = ['baga'.'hentai'.'urusai'];
const vnode = h("ul".null, items.map((item, index) = > h("li", {
key: index
}, item)));
const list = render(vnode);
document.body.appendChild(list);
Copy the code
With h render two functions, we have implemented a very simple JSX renderer!!
reference
-
WTF is JSX
-
JSX In Depth
-
React Without JSX