首页 > 其他分享 >react的jsx语法是怎样解析的

react的jsx语法是怎样解析的

时间:2022-09-26 09:46:18浏览次数:82  
标签:语法 React babel react createElement div jsx children

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

  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 --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做了什么?

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").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 = <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);

标签:语法,React,babel,react,createElement,div,jsx,children
From: https://www.cnblogs.com/xiatianweidao/p/16729803.html

相关文章

  • reactor的三种模式
    Reactor响应式编程,是NIO的编程设计模式 单reactor单线程模式:简单NIO例子中,选择器循环和业务处理线程都用一个线程。也是最简单的NIO编程模式。   单Reacto......
  • react脚手架搭建的几种方式
    react脚手架创建几种方式nodev16.17.0npm8.15.0yarn3.2.3vite3.1.3配置less-loadernpm首先通过npm方式全局安装create-react-appnpminstall-gcrea......
  • 三--词法分析及语法分析程序--1.设计扫描器时应考虑的几个问题
     1.词法分析器的功能与实现方式  2.源程序的输入及预处理3.单词符号的内部表示--词法分析器的输出形式......
  • python语法之注释
    引言注释的最大作用是提高程序的可读性,在开发过程中非常有必要加上注释。Python支持两种类型的注释,分别是单行注释和多行注释。1单行注释Python使用井号#作为单行注......
  • Java基础语法 三元运算符
    格式优先级packageBasicGrammar.day03;/*运算符之五:位运算符(了解)结论:1.位运算符操作的都是整型的数据2.<<:在一定范围内,每向左移1位,相当于*2>>:在一......
  • React 入门学习笔记
    npxcreate-react-appdemo创建reactdemo,此命令行不需要提前安装create-react-app脚手架,创建demo后就会删除了vue微信小程序都是MVVM框架react是MVC框架jsx......
  • Java基础语法 位运算符
    定义结论:1.位运算符操作的都是整型的数据2.<<:在一定范围内,每向左移1位,相当于*23.>>:在一定范围内,每向右移1位,相当于/2packageBasicGrammar.day03;/*运算符......
  • 一文读透react精髓
    学和使用react有一年多了,最近想在梳理一下react基础知识,夯实基础,激流勇进~关于reacr-router,redux,redux-saga后续都会慢慢输出,希望各位看官老爷持续关注~~要是能给个赞......
  • React 函数式组件怎样进行优化
    前言目的本文只介绍函数式组件特有的性能优化方式,类组件和函数式组件都有的不介绍,比如key的使用。另外本文不详细的介绍API的使用,后面也许会写,其实想用好hooks还是......
  • C# 二十年语法变迁之 C# 2,C# 3 ,C# 4参考
    C#二十年语法变迁之C#2,C#3,C#4参考https://benbowen.blog/post/two_decades_of_csharp_i/自从C#于2000年推出以来,该语言的规模已经大大增加,我不确定任何人是......