首先咱们来看看上面的代码

  import "react" from "react";  const element = (<div>        <div>            <span>1</span>            <span>2</span>            <span>3</span>        </div>        <div>1</div>        <div>2</div></div>)console.log(element)

问题来了,element是如何输入上图所示的构造的?

环境配置

装置reactbabel

npm i react react-dom --savenpm i @babel/core @babel/preset-env @babel/plugin-transform-react-jsx --save-dev

配置babel

{    test: /\.(js|jsx)$/,    include: paths.appSrc,    loader: require.resolve('babel-loader'),    options: {        {            "presets": [                "@babel/preset-env"            ],            "plugins": [                "@babel/plugin-transform-react-jsx"            ]        },        cacheDirectory: true,    }}

@babel/plugin-transform-react-jsx做了什么?

React实战视频解说:进入学习

遇到    <div>123</div>执行React.createElement("div", "123");遇到    <div>        <div>1</div>        <div>2</div>        <div>3</div>    </div>执行    React.createElement("div",         React.createElement("div", "1"),        React.createElement("div", "2"),        React.createElement("div", "3")    )// 也就是说,用react开发的时候只有你用到了jsx语法,那么不论你有没有用到React都必须import react from "react"

写个函数来模仿它的执行过程

为了便于了解 咱们把<div>    <div>        <span>1</span>        <span>2</span>        <span>3</span>    </div>    <div>1</div>    <div>2</div></div>当做一棵树let element = {    type:"div",    children:[{        type:"div",        children:[{            type:"span",            children:"1"        }, {            type:"span",            children:"2"        }, {            type:"span",            children:"3"        }]    }, {        type:"div",        children:1    }, {        type:"div",        children:2    }]}写一个函数对这颗树进行深度遍历function jsxTransformNode(element, callback){    let children = [];    if (Array.isArray(element.children)) {          children = element.children.map(child => jsxTransformNode(child, callback))    } else {        children = [element.chidren]    }    return callback(element.type, ...children);}let nodes = jsxTransformNode(child, function ReactCreateElement(type, ...children){    return {        tag: type,        children    }}) 

@babel/plugin-transform-react-jsx的原理

babel不熟的话能够先看这边文章从零开始编写一个babel插件

它其实就是将

<div className="name" age="12">    <div>1</div>    <div>2</div>    <div>3</div></div>转化为React.createElement(    "div",    {},    React.createElement("div", {}, ...chidren),    React.createElement("div", {}, ...chidren),    React.createElement("div", {}, ...chidren))代码块

废话不多说间接上代码,上面是我写的一个简略的babel-plugin来对jsx语法进行解析

var generator = require("@babel/generator").defaultfunction buildAttrsCall (attribs, t){    let properties = [];    attribs.forEach(attr => {        let name = attr.name.name;        let value = attr.value;        properties.push(t.objectProperty(t.stringLiteral(name), value))    });    return t.ObjectExpression(properties);}const createVisitor = (t) => {    const visitor = {};    visitor.JSXElement = {        // 为什么是exit,因为jsx是DFS而不是BFS;        exit(path, file){            let openingPath = path.get("openingElement");            let children = t.react.buildChildren(openingPath.parent);            let tagNode = t.identifier(openingPath.node.name.name);            // 创立React.createElement            let createElement =  t.memberExpression(t.identifier("React"),t.identifier("createElement"));            // 创立属性            let attribs = buildAttrsCall(openingPath.node.attributes, t);            // 创立React.createElement(tag, attrs, ...chidren)表达式            let callExpr = t.callExpression(createElement, [tagNode, attribs, ...children]);            path.replaceWith(t.inherits(callExpr, path.node));        }    }    return {        visitor,        // 配置jsx解析器        inherits:() => {            return {                manipulateOptions(opts, parserOpts) {                    parserOpts.plugins.push("jsx");                }            };        }    }}module.exports = function(babel){    const t = babel.types;    return createVisitor(t);}
  1. 创立tagNode变量
  2. 创立React.createElement表达式
  3. 创立attribs对象
  4. 创立React.createElement("div", {}, ...children)表达式
  5. 最初替换node

成果如下

源代码如下

const a = <div className="name" age="12">    <div>1</div>    <div>2</div>    <div>3</div></div>;

编译之后

var a = React.createElement(div, {  "className": "name",  "age": "12"}, React.createElement(div, {}, "1"), React.createElement(div, {}, "2"), React.createElement(div, {}, "3"));console.log(a);