react中的jsx 与 babel插件原理

954 阅读2分钟

react中的jsx 与 babel插件原理

  • @babel/plugin-transform-react-jsx插件原理

1.初始化项目


mkdir jsx-transformer

cd jsx-transformer

yarn add @babel/core @babel/plugin-syntax-jsx @babel/plugin-transform-react-jsx @babel/types --dev

yarn add react

2.JSX

  • React使用JSX来描述用户界面

  • JSX是一种JavaScript的语法扩展

  • babeljs.io/repl可以在线转换代码

  • astexplorer可以把代码转换成AST树


<h1 id="title" key="title" ref="title">hello</h1>

3. AST抽象语法树

抽象语法树(Abstract Syntax Tree,AST)是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构

ast.jpeg

3.1 babel工作流

  • Parse(解析) 将源代码转换成抽象语法树,树上有很多的节点

  • Transform(转换) 对抽象语法树进行转换

  • Generate(代码生成) 将上一步经过转换过的抽象语法树生成新的代码

ast-compiler-flow.jpg

3.2 babel处理语法树


let babel = require('@babel/core');

let types = require('@babel/types');

let traverse = require("@babel/traverse").default;

let generate = require("@babel/generator").default;

const code = `function ast() {}`;

const ast = babel.parse(code);

let indent = 0;

const padding = ()=>" ".repeat(indent);

traverse(ast, {

enter(path){

console.log(padding()+path.node.type+'进入');

indent+=2;

if(types.isFunctionDeclaration(path.node)){

path.node.id.name = 'newAst';

}

},

exit(path){

indent-=2;

console.log(padding()+path.node.type+'离开');

}

});

const output = generate(ast,{},code);

console.log(output.code);

3.3 旧转换

3.3.1 jsx.js


const babel = require("@babel/core");

const sourceCode = `<h1 id="title" key="title" ref="title">hello</h1>`;

const result = babel.transform(sourceCode, {

plugins: [['@babel/plugin-transform-react-jsx',{runtime:'classic'}]]

});

console.log(result.code);

3.3.2 转译结果


let React = require('react');

React.createElement("h1", {

id: "title",

key: "title",

ref: "title"

}, "hello");

console.log(JSON.stringify(element,replacer,2));

function replacer(key,value){

if(!['_owner','_store'].includes(key))

return value;

}


{

"type": "h1",

"key": "title",

"ref": "title",

"props": {

"id": "title",

"children": "hello"

}

}

3.4 新转换

3.4.1 jsx.js


const babel = require("@babel/core");

const sourceCode = `<h1 id="title" key="title" ref="title">hello</h1>`;

const result = babel.transform(sourceCode, {

plugins: [['@babel/plugin-transform-react-jsx',{runtime:'automatic'}]]

});

console.log(result.code);

3.4.2 转译结果


let {jsx:_jsx} = require("react/jsx-runtime");

//import { jsx as _jsx } from "react/jsx-runtime";

let element = _jsx("h1", {id: "title",key:"title",ref:"title",children: "hello"}, "title");

console.log(JSON.stringify(element,replacer,2));

function replacer(key,value){

if(!['_owner','_store'].includes(key))

return value;

}


{

"type": "h1",

"key": "title",

"ref": "title",

"props": {

"id": "title",

"children": "hello"

}

}

4.实现插件

  • babel-types文档

  • babel插件开发指南

  • @babel/plugin-syntax-jsx允许解析JSX

  • Visitors(访问者)是一个用于 AST 遍历的模式,用于定义某个节点的操作方法

  • Paths(路径) 是一个对象,它表示两个节点之间的连接

  • replaceWith可以用于替换节点

  • get用于查找特定类型的子路径

  • find 用于向上查找一个指定条件的路径

  • unshiftContainer用于把AST节点插入到类似于body这样的数组中

  • Scope(作用域)

  • generateUidIdentifier会生成一个不会和任何本地定义的变量冲突的标识符

5. 代码地址参考

仓库地址