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)是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构
3.1 babel工作流
-
Parse(解析) 将源代码转换成抽象语法树,树上有很多的节点
-
Transform(转换) 对抽象语法树进行转换
-
Generate(代码生成) 将上一步经过转换过的抽象语法树生成新的代码
3.2 babel处理语法树
-
babeljs是一个JavaScript编译器
-
@babel/core是Babel编译器的核心
-
babylon 是Babel使用的JavaScript解析器
-
@babel/types 用于 AST 节点的 Lodash 式工具库
-
@babel/traverse 用于对 AST 的遍历,维护了整棵树的状态,并且负责替换、移除和添加节点
-
@babel/generator 把AST转成代码
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/plugin-syntax-jsx允许解析JSX
-
Visitors(访问者)是一个用于 AST 遍历的模式,用于定义某个节点的操作方法
-
Paths(路径) 是一个对象,它表示两个节点之间的连接
-
replaceWith可以用于替换节点
-
get用于查找特定类型的子路径
-
find 用于向上查找一个指定条件的路径
-
unshiftContainer用于把AST节点插入到类似于body这样的数组中
-
Scope(作用域)
-
generateUidIdentifier会生成一个不会和任何本地定义的变量冲突的标识符