一、工作原理 AST
Babel 本质上就是在操作 AST 来完成代码的转译。
1.1 是什么
// es2015 的 const 和 arrow function
const add = (a, b) => a + b;
// Babel 转译后
var add = function add(a, b) {
return a + b;
};
Babel 的功能很纯粹。我们传递一段源代码给 Babel,然后它返回一串新的代码给我们。就是这么简单,它不会运行我们的代码,也不会去打包我们的代码。它只是一个编译器。
1.2 Babel解析过程
- Parse(解析) :将源代码转换成AST(抽象语法树)。 @babel/parser
- Transform(转换) :对于 AST 进行变换一系列的编辑操作。
- @babel/traverse可以通过「深度优先」的方式遍历AST树
- 对于遍历到的每条路径-@babel/types提供用于修改AST节点的节点类型数据。
- Generate(代码生成) :将第二步将转换过的AST输出。@babel/generator
@babel/core 模块则是将三者结合使得对外提供的API做了一个简化。
1.3 整个Babel底层编译能力
babel-core提供了以上提到的三个步骤的能力。
@babel/core(由 @babel/parser、 @babel/traverse、 @babel/types、 @babel/generator等组成),他们提供了Babel编译JS的能力。
二、AST
babel默认是使用Esprima去解析AST的。
例如:
解析:
一个 AST 的根节点始终都是 Program,上面的例子我们从 declarations 开始往下读:
一个VariableDeclaration(变量声明):声明了一个 name 为 add 的ArrowFunctionExpression(箭头函数):
- params(函数入参):a 和 b
- 函数体:函数主体是一个BinaryExpression(二项式),一个标准的二项式分为三部分:
left(左边):aoperator(运算符):加号 +right(右边):b
三、babel工作过程
3.1 Parse(解析)
Parse 阶段可以细分为两个阶段:词法分析(Lexical Analysis, LA)和语法分析(Syntactic Analysis, SA)。
(1)词法分析:
词法分析阶段可以看成是对代码进行“分词”,它接收一段源代码,然后执行一段 tokenize 函数,把代码分割成被称为Tokens 的东西。会移除空白符、注释等。
Tokens 是一个一纬数组,由一些代码的碎片组成,比如数字、标点符号、运算符号等等等等,
- Keyword: 关键字
- Identifier: 唯一标志, 标记
- Punctuator: 标点符号
- Numeric: 数字
(2)语法分析:
词法分析之后,代码就已经变成了一个 Tokens 数组了,现在需要通过语法分析把 Tokens 转化为树形的形式即 AST。
同时,验证语法。语法如果有错的话,抛出语法错误。
生成树的时候,解析器会删除一些没必要的标识 tokens(比如:不完整的括号),因此 AST 不是 100% 与源码匹配的。
3.2 Transform(转换)
AST 中有很多相似的元素,它们都有一个 type 属性,这样的元素被称作节点。一个节点通常含有若干属性,可以用于描述 AST 的部分信息。
比如这是一个最常见的 Identifier 节点:
{
type: 'Identifier',
name: 'add'
}
表示这是一个标识符。
所以,操作 AST 也就是操作其中的节点,可以增删改这些节点,从而转换成实际需要的 AST。
ps: Babel 对于 AST 的遍历是深度优先遍历,对于 AST 上的每一个分支 Babel 都会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的节点,然后寻找下一个分支。
3.3 Generate(代码生成)
经过上面两个阶段,需要转译的代码已经经过转换,生成新的 AST 了,最后一个阶段理所应当就是根据这个 AST 来输出代码。
结尾:经过这三个阶段,代码就被 Babel 转译成功了。
四、推荐工具
- AST Explorer 在线生成 AST。
- Esprima 可以查看分词结果。