[关闭]
@Dale-Lin 2022-09-18T22:01:12.000000Z 字数 2599 阅读 387

Babel处理阶段

Babel


Babel 三个主要处理阶段分为:parse(解析),transform(变换),generate(生成)。


Parse

Parse 阶段,babel(@babel/parser)将代码转成 AST,进一步分为词法分析和语法分析。

Lexical Analysis

将代码转成 stream of tokens。

tokens 可以理解为一组扁平的语法片段数组

  1. n * n;
  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. ]

每个 type 属性包含一组 token 的属性描述:

  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. }

Syntactic Analysis

语法分析会把 stream of tokens 转换成 AST 的表示形式,利用了 tokens 的信息。AST 能更好的表示出代码的结构。


Transform

transform 阶段遍历 AST,并添加/更新/删除节点。
这是编译工具处理过程中最复杂的部分,也是插件操作的部分。


Generate

generate 阶段将 AST 转换回代码字符串,同时生成 source map。

生成阶段很简单,深度优先遍历 AST,同时构建表示转换后代码的字符串。


遍历

要对 AST 节点做变换必须递归遍历整棵树。

对每个 type 的节点,要知道它的结构和属性,然后按顺序深度优先遍历。

visitors

visitors 是 AST 遍历的术语,简单看做一个对象,具有能接受不同 type 的节点的方法,例如:

  1. const MyVisitor = {
  2. Identifier() {
  3. console.log('called!');
  4. }
  5. }
  6. // 可以后续继续添加方法
  7. // MyVisitor.MemberExpression = function() {};

Identifier() {...}Identifier: { enter() {...} } 的简写。

上面的 visitor 会对每个 Identifier 节点做处理。

通常情况下,visitor 的方法都会在节点 enter 的时候调用,但也可以在 exit 的时候调用,例如:

  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. }

转换成树结构

  1. - FunctionDeclaration
  2. - Identifier (id)
  3. - Identifier (params[0])
  4. - BlockStatement (body)
  5. - ReturnStatement (body)
  6. - BinaryExpression (argument)
  7. - Identifier (left)
  8. - Identifier (right)

遍历步骤会是深度优先队列(先进先出),所以 visitor 的每个 type 的方法可以有两次访问节点的时机:

  1. const MyVisitor = {
  2. Identifier: {
  3. enter() {
  4. console.log('Entered!');
  5. },
  6. exit() {
  7. console.log('Exited!');
  8. }
  9. }
  10. }

必要时可以一个函数处理多种 type 的节点,只要用 | 隔开:

  1. const MyVisitor = {
  2. "ExportNamedDeclaration|Flow"(path) {}
  3. }

babel-types 定义了一些类型的别名,例如:
Function 等于 FunctionDeclaration|FunctionExpression|ArrowFunctionExpression|ObjectMethod|ClassMethod

  1. const MyVisitor = {
  2. Function(path) {}
  3. }

Path

Path 是用来链接 AST 上节点之间的桥梁,一个 Path 是一个表示两个节点之间连接的对象,例如:

一个具有子节点的节点:

  1. {
  2. type: "FunctionDeclaration",
  3. id: {
  4. type: "Identifier",
  5. name: "square"
  6. },
  7. ...
  8. }

用 Path 表示:

  1. {
  2. "parent": {
  3. "type": "FunctionDeclaration",
  4. "id": {...},
  5. ...
  6. },
  7. "node": {
  8. "type": "Identifier",
  9. "name": "square"
  10. }
  11. }

path 还包含额外的一些源信息:

  1. {
  2. "parent": {...},
  3. "node": {...},
  4. "hub": {...},
  5. "contexts": [],
  6. "data": {},
  7. "shouldSkip": false,
  8. "shouldStop": false,
  9. "removed": false,
  10. "state": null,
  11. "opts": null,
  12. "skipKeys": null,
  13. "parentPath": null,
  14. "context": null,
  15. "container": null,
  16. "listKey": null,
  17. "inList": false,
  18. "parentKey": null,
  19. "key": null,
  20. "scope": null,
  21. "type": null,
  22. "typeAnnotation": null
  23. }

并且具有非常多的与添加/更新/移动/删除节点的方法。

Paths 是响应式的,任何时候调用方法更新了 AST,paths 信息都会更新。

Paths 是响应式的,但AST 对象不是响应式的。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注