导航
Babel 的转译过程分为三个阶段,这三步具体是:
babel-traverse
对其进行遍历,在此过程中进行添加、更新及移除等操作babel-generator
Babel 解析成 AST,然后插件更改 AST,最后由 Babel 输出代码 。
那么 Babel 的插件模块需要你暴露一个 function
,function
内返回 visitor
module.exports = function(babel) {
return {
visitor: {}
}
}
visitor
是对各类型的 AST 节点做处理的地方,那么我们怎么知道 Babel 生成了的 AST 有哪些节点呢?
很简单,你可以把 Babel 转换的结果打印出来,或者这里有传送门: AST explorer
这里我们看到 const result = 1 + 2
中的 1 + 2
是一个 BinaryExpression
节点,那么在 visitor
中,我们就处理这个节点:
const t = require('babel-types');
const visitor = {
BinaryExpression(path) {
const node = path.node;
let result;
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
switch(node.operator) {
case "+":
result = node.left.value + node.right.value;
break;
case "-":
result = node.left.value - node.right.value;
break;
case "*":
result = node.left.value * node.right.value;
break;
case "/":
result = node.left.value / node.right.value;
break;
case "**":
let i = node.right.value;
while (--i) {
result = result || node.left.value;
result = result * node.left.value;
}
break;
default:
}
}
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
}
}
}
module.exports = function(babel) {
return {
visitor
}
}
插件写好了,我们运行下插件试试:
const babel = require('babel-core');
const result = babel.transform("const result= 1 + 2;", {
plugins: [
require('./index')
]
})
console.log(result.code); // const result = 3;
与预期一致,那么转换 const result = 1 + 2 + 3 + 4 + 5;
呢?
结果是: const result = 3 + 3 + 4 + 5;
这就奇怪了,为什么只计算了 1 + 2 之后,就没有继续往下运算了? 我们看一下这个表达式的 AST 树
你会发现 Babel 解析成表达式里面再嵌套表达式。
表达式( 表达式( 表达式( 表达式(1 + 2) + 3) + 4) + 5)
而我们的判断条件并不符合所有的,只符合 1 + 2
那么我们得改一改
第一次计算 1 + 2 之后,我们会得到这样的表达式
表达式( 表达式( 表达式(3 + 3) + 4) + 5)
其中 3 + 3 又符合了我们的条件, 我们通过向上递归的方式遍历父级节点
又转换成这样:
表达式( 表达式(6 + 4) + 5)
表达式(10 + 5)
15
修改代码:
if (result !== undefined) {
// 把表达式节点替换成 number 字面量
path.replaceWith(t.numericLiteral(result));
let parentPath = path.parentPath;
// 向上遍历父级节点
parentPath && visitor.BinaryExpression.call(this, parentPath);
}
到这里,我们就得出了结果 const result = 15;
jsconfig.json
):如果你没有 jsconfig.json
,可以在项目根目录下创建:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
tsconfig.json
):如果你使用的是 TypeScript,可以在 tsconfig.json
中添加:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}