Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不同形式的静态分析。
静态分析是在不需要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析即是动态分析)。 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。
babel 在处理代码时经历的过程: 1、解析(parse)。将源代码变成 AST。 2、转换(transform)。操作 AST,这也是我们可以操作的部分,去改变代码。 3、生成(generate)。将更改后的 AST,再变回代码。
我们写 Babel 插件的时候,最重要的就是要定义一个 visitor 对象。 这是因为 Babel 插件的设计遵循访问者(Visitor)模式。什么是访问者模式呢?简单来说,访问者模式是能把处理方法从数据结构中分离出来的一种模式,这种模式的好处就是可以根据需求增加新的处理方法,且不用修改原来的程序代码与数据结构,而处理方法都是内部具体实现了,是依赖反转原则的反面
npm i @babel/core
使用babel.transformSync
,两个入参,第一个为 code 字符串,第二个为 options,和.babelrc 一样
presets 就是一组 plugins 的集合,所以都是插件
在 plugins 里可以引入相对路径的插件地址
plugins: ["./babel/index"],
插件遵循这样的执行顺序,pre -> visitor -> post,而 pre 与 post 只接受一个入参 state,以下有对其说明,不能操作 ast,而 visitor 的节点 都有 enter 和 exit 的过程,默认使用 enter 阶段。
module.exports = ({ types: t }) => {
return {
pre(state) {
this.isOp = false;
},
post(state) {
this.isOp = null;
},
visitor: {
BlockStatement: {
enter(path) {
console.log(path);
},
exit(path) {
console.log(path);
}
},
ImportDeclaration() {
console.log("ImportDeclaration");
},
},
}
插件参数是个 babel 对象,能拿到@babel/types
,主要用途是在构建 AST 和检查 AST 节点类型的方法
如:
module.exports = (babel) => {
const { types } = babel;
return {
visitor: {
ImportDeclaration() {
console.log('ImportDeclaration');
},
},
};
};
而如何生成节点,可以根据辅助工具AST Explorer还有 api 文档对应的节点入参进行编写
编写插件主要是编写 visitor 对象,即告诉遍历 ast 时要访问哪些类型的代码,以及对这些代码要做的操作。
而想知道 ast 就需要辅助工具进行转换,如AST Explorer,然后从中获取到对应节点的 type
比如导入的 typeImportDeclaration
在访问对应的节点时,接收两个对象 path 与 state
state 是该 ast 的数据源的数据,如来自哪个文件、执行位置、source、整个 ast 等等
而 babel 插件的编写过程就是个使用辅助工具找到需要操作的节点,然后 visitor 添加此节点进行你想要的操作,如下图
path 上有很多工具方法,如替换节点,查找父节点,查找节点之类的,详细看文档
ast 树的遍历是深度优先遍历,而我们常知道的前序中序后序都是深度优先遍历,而 ast 的遍历类似前序遍历,而从文件上看就是从上到下执行,拿到节点后把节点内的所有子节点递归的形式遍历
data-if
指令实现
module.exports = ({ types: t }) => {
return {
visitor: {
JSXAttribute(path) {
// data-if 指令
if (path.node.name.name === 'data-if') {
const parent = path.findParent((p) => {
return p.isJSXElement();
});
if (t.isJSXElement(parent.parentPath)) {
parent.replaceWith(
t.jsxExpressionContainer(
t.conditionalExpression(path.node.value.expression, parent.node, t.nullLiteral()),
),
);
} else if (path.node.value.type === 'JSXExpressionContainer') {
parent.replaceWith(t.conditionalExpression(path.node.value.expression, parent.node, t.nullLiteral()));
}
path.remove();
}
},
},
};
};
使用:
<Test2 data-if={props.vis} />
使用 vscode 生成launch.json
,node 执行修改下program
执行位置,若 npm 添加runtimeExecutable
与runtimeArgs
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"]
}
]
}