JSX是一种JavaScript的语法拓展,可使用它来进行UI的展现:javascript
const element = <h1>Hello, world!</h1>;
咱们通常会在组件的render
方法里使用JSX进行布局和事件绑定:html
class Home extends Component { render() { return ( <div onClick={() => console.log('hello')}> <h1>Hello, world!</h1> <Blog title="deepred" /> </div> ); } }
React的核心机制之一就是能够建立虚拟的DOM元素,利用虚拟DOM来减小对实际DOM的操做从而提高性能,JSX正是为了虚拟DOM而存在的语法糖java
咱们在平时的组件编写中,一般都这么写:node
import React, { Component } from 'react'; class Demo extends Component { render() { return ( <h1>Hello, world!</h1> ) } }
然而代码里面并无用到React,为何要引入这个变量呢?react
由于JSX是React.createElement
这个方法的语法糖:webpack
const element = <h1 id="container" className="home">Hello</h1>; // 等价于 const element = React.createElement("h1", { id: "container", className: "home" }, "Hello");
推荐你们在babeljs.io上看下JSX编译后的实际效果web
React.createElement有三个参数:api
React.createElement( type, // dom类型,好比div,h1 [props], // dom属性,好比id,class,事件 [...children] // 子节点,字符串或者React.createElement生成的一个对象 )
JSX用一种相似HTML的语法替代了比较繁琐的React.createElement
纯JS方法,而@babel/preset-react
插件就起到了最关键的一步:负责在webpack编译时,把全部的JSX都改为React.createElement
:数组
class Home extends Component { render() { return ( <div onClick={() => console.log('hello')}> <h1>Hello, world!</h1> <Blog title="deepred" /> </div> ); } }
编译后:babel
class Home extends Component { render() { return React.createElement("div", { onClick: () => console.log('hello') }, React.createElement("h1", null, "Hello, world!"), React.createElement(Blog, { title: "deepred" })); } }
在开发中,有了JSX后咱们基本不怎么须要用到createElement
方法,但若是咱们须要实现这样一个组件:
// 根据传入的type属性,渲染成相应的html元素 <Tag type="h1" id="hello" onClick={() => console.log('hello')}>this is a h1</Tag> <Tag type="p">this is a p</Tag>
咱们不太可能根据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> } }
这时,就须要用到底层的api了:
function Tag(props) { const { type, ...other } = props; return React.createElement(type, other, props.children); }
虚拟dom本质就是一个js对象:
const vnode = { tag: 'div', attrs: { className: 'container' }, children: [ { tag: 'img', attrs: { src: '1.png' }, children: [] }, { tag: 'h3', attrs: {}, children: ['hello'] } ] }
能够经过在每一个文件的上方添加/** @jsx h */
来告诉@babel/preset-react
用h
方法名代替JSX(默认方法是React.createElement)
/** @jsx h */ const element = <h1 id="container" className="home">Hello</h1>;
/** @jsx h */ const element = h("h1", { id: "container", className: "home" }, "Hello");
如今让咱们开始建立本身的h
函数吧!
function h(nodeName, attributes, ...args) { // 使用concat是为了扁平化args,由于args数组里面的元素可能也是数组 // h('div', {}, [1, 2, 3]) h('d', {}, 1, 2, 3) 都是合法的调用 const children = args.length ? [].concat(...args) : null; return { nodeName, attributes, children }; }
const vnode = h("div", { id: "urusai" }, "Hello!"); // 返回 // { // "nodeName": "div", // "attributes": { // "id": "urusai" // }, // "children": [ // "Hello!" // ] // }
h
的做用就是返回一个vnode,有了vnode,咱们还须要把vnode转成真实的dom:
function render(vnode) { if (typeof vnode === 'string') { // 生成文本节点 return document.createTextNode(vnode); } // 生成元素节点并设置属性 const node = document.createElement(vnode.nodeName); const attributes = vnode.attributes || {}; Object.keys(attributes).forEach(key => node.setAttribute(key, attributes[key])); if (vnode.children) { // 递归调用render生成子节点 vnode.children.forEach(child => node.appendChild(render(child))); } return node; }
如今让咱们使用这两个方法吧:
/** @jsx h */ const vnode = <div id="urusai">Hello!</div>; const node = render(vnode); document.body.appendChild(node);
编译转码后:
/** @jsx h */ const vnode = h("div", { id: "urusai" }, "Hello!"); const node = render(vnode); document.body.appendChild(node);
咱们还能够遍历数组:
/** @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);
编译转码后:
/** @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);
经过h
render
两个函数,咱们就实现了一个很简单的JSX渲染器!!!