react的jsx语法是怎样解析的

首先我们来看看下面的代码

  import "react" from "react";
  const element = (
1 2 3
1
2
) console.log(element)

react的jsx语法是怎样解析的_第1张图片

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

环境配置

安装reactbabel

npm i react react-dom --save
npm 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做了什么?

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

参考React实战视频讲解:进入学习

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

为了便于理解 我们把
1 2 3
1
2
当做一棵树 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 } })

react的jsx语法是怎样解析的_第2张图片

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

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

它其实就是将

1
2
3
转化为 React.createElement( "div", {}, React.createElement("div", {}, ...chidren), React.createElement("div", {}, ...chidren), React.createElement("div", {}, ...chidren) ) 代码块

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

var generator = require("@babel/generator").default
function 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 = 
1
2
3
;

编译之后

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

你可能感兴趣的:(react.js)