Babel 简介

Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不同形式的静态分析。

静态分析是在不需要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析即是动态分析) 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。

image.png

基础

Babel 是 JavaScript 编译器,更确切地说是源码到源码的编译器,通常也叫做“转换编译器”。
意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。

抽象语法树(AST)

这个处理过程中的每一步都涉及到创建或是操作抽象语法树,亦称 AST。

Babel 使用一个基于 ESTree 并修改过的 AST。 ESTree:ECMAScript 标准的 AST。 Babel AST:Babel 自己的 AST 标准。

我们来看一段 JS 代码 和 抽象语法树,感受一下。在线链接

  1. function square(n) {
  2. return n * n;
  3. }

转换后的 AST,可以用两种形式查看。

  1. - FunctionDeclaration:
  2. - id:
  3. - Identifier:
  4. - name: square
  5. - params [1]
  6. - Identifier
  7. - name: n
  8. - body:
  9. - BlockStatement
  10. - body [1]
  11. - ReturnStatement
  12. - argument
  13. - BinaryExpression
  14. - operator: *
  15. - left
  16. - Identifier
  17. - name: n
  18. - right
  19. - Identifier
  20. - name: n
  1. {
  2. type: "FunctionDeclaration",
  3. id: {
  4. type: "Identifier",
  5. name: "square"
  6. },
  7. params: [{
  8. type: "Identifier",
  9. name: "n"
  10. }],
  11. body: {
  12. type: "BlockStatement",
  13. body: [{
  14. type: "ReturnStatement",
  15. argument: {
  16. type: "BinaryExpression",
  17. operator: "*",
  18. left: {
  19. type: "Identifier",
  20. name: "n"
  21. },
  22. right: {
  23. type: "Identifier",
  24. name: "n"
  25. }
  26. }
  27. }]
  28. }
  29. }

你会留意到 AST 的每一层都拥有相同的结构:

  1. {
  2. type: "FunctionDeclaration",
  3. id: {...},
  4. params: [...],
  5. body: {...}
  6. }
  1. {
  2. type: "Identifier",
  3. name: ...
  4. }
  1. {
  2. type: "BinaryExpression",
  3. operator: ...,
  4. left: {...},
  5. right: {...}
  6. }

这样的每一层结构也被叫做 节点(Node)。 一个 AST 可以由单一的节点或是成百上千个节点构成。
它们组合在一起可以描述用于静态分析的程序语法。
每一个节点都有如下所示的接口(Interface):

字符串形式的 type 字段表示节点的类型(FunctionDeclaration``Identifier``BinaryExpression等) 每一种类型的节点定义了一些附加属性用来进一步描述该节点类型。

  1. interface Node {
  2. type: string;
  3. }

Babel 还为每个节点额外生成了一些属性,用于描述该节点在原始代码中的位置。

每一个节点都会有 start,end,loc 这几个属性。

  1. {
  2. type: ...,
  3. start: 0,
  4. end: 38,
  5. loc: {
  6. start: {
  7. line: 1,
  8. column: 0
  9. },
  10. end: {
  11. line: 3,
  12. column: 1
  13. }
  14. },
  15. ...
  16. }

Babel 处理步骤

Babel 的三个主要处理步骤分别是:解析(parse)转换(transform)生成(generate)

解析

这个过程接收代码并输出 AST。分为两个阶段“词法分析”和“语法分析”。

词法分析

词法分析阶段把字符串形式的代码转换为 令牌(tokens) 流。.
你可以把令牌看作是一个扁平的语法片段数组。比如下面代码转成 tokens

  1. n * n

和 AST 节点一样,token 也有 start,end,loc 属性。.

  1. [
  2. { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
  3. { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
  4. { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
  5. ...
  6. ]
  1. {
  2. type: {
  3. label: 'name',
  4. keyword: undefined,
  5. beforeExpr: false,
  6. startsExpr: true,
  7. rightAssociative: false,
  8. isLoop: false,
  9. isAssign: false,
  10. prefix: false,
  11. postfix: false,
  12. binop: null,
  13. updateContext: null
  14. },
  15. ...
  16. }

语法分析

语法分析阶段会把一个令牌流转换成 AST 的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的表述结构,这样更易于后续的操作。

转换

转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。
这是 Babel 或是其他编译器中最复杂的过程,同时也是插件将要介入工作的部分。

生成

代码生成步骤把最终的 AST 深度优先遍历转换成字符串形式的代码,同时还会创建源码映射(source maps)

参考资料

《Babel 插件手册》
《AST 在线转换》