Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

了解 Babel #13

Open
sundway opened this issue Sep 27, 2017 · 0 comments
Open

了解 Babel #13

sundway opened this issue Sep 27, 2017 · 0 comments
Assignees
Labels

Comments

@sundway
Copy link
Owner

sundway commented Sep 27, 2017

最近看了一些 babel 以及 JS 编译器相关的东西,后面分系列对每部分做深入了解。作为第一篇文章,这篇文章总结一些我对 Babel 的整体了解。

一个简单的 JS 编译器

首先可以先看一下一个最简单的 JS 编译器:the-super-tiny-compiler。它分为一下几个步骤:

  • 解析:解析这一步主要是把 JS 代码转换成 AST(抽象语法树),很多语言的编译过程也都会有这样一个概念。而这里面主要做了两步操作:词法分析(Tokenizer or Lexer)语法分析(Parse)。词法分析作用相当于自然语言的分词,比如 a + b,a 和 b 此时都会输出类似 Identifier 这样词法元素,并且一些无效的元素(取决于语言设计)会被剔除。它输出的是扁平化结构, 一般是一个 token 队列结构, 或是一个 token Stream 供 Parser 使用。语法解析通常是根据生成的 token 队列输出是一个树形结构,实现上会涉及大量的递归操作。通常这些解析过程我们可以利用一些工具帮助分析如[AST Explorer],还有其他很多类似的工具。(https://astexplorer.net/) 如图:

  • 转化:转化主要是对 AST 的节点进行 adding/removing/replacing 操作。而在 babel 中,很多插件就是在这一步根据做各种操作,最终静态编译出我们想要的代码。

  • 代码生成:大多数编译代码生成这一步主要是将 AST 和字符串化的代码返回出去。看 babel-core 中的回调也是一样,最终返回了三个参数 code、 map、ast。其中 map 主要是为了方便调试引入的 source map。

以上是这个超级简单的编译器编译的几个主要步骤,但是仔细看,其中还有几个关键的概念:

  • Visitors: 在转化 AST 需要对它进行递归的树形遍历。在遍历的过程中,实际就是通过 visitors(我的理解是主要用来操作节点一种模式)这种模式对节点进行访问,使用这个名字也主要是因为它采用的访问者模式,我们后面写 babel 插件时实际就是写的访问者。比如以下这个
function square(n) {
  return n * n;
}


比如我们需要对 Identifier 进行操作。

const MyVisitor = {
  Identifier() {
    console.log("Called!");
  }
};

// 你也可以先创建一个访问者对象,并在稍后给它添加方法。
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}

以上就是一个访问者,当进入到 identifier(关于 identifier 可以看看 babylon 语法规范) 节点时,就会自动调用 Identifier() 方法,这里每次进入以及出节点和退出节点都会调用一次方法。

Babel 的编译过程

babel 的编译过程主要是通过 babel-core 来实现的,所以只需要看看 babel-core 是如何做上面的编译过程。

以上是 babel-core 中的依赖,重点几个东西:

  • babylon 是比较核心的一部分,主要就是相当于上面的解析操作,它严重依赖 acorn 和 acorn-jsx 这两个库。
  • babel-traverse 是最为复杂的一部分,主要进行了上面的转化操作,对 AST 节点进行增加、删除、替换操作。
  • babel-generator 是较为简单的一部分,主要进行了上面的代码生成操作,将 AST 转换成代码。

其他的一些库则是一些辅助库,涉及到对节点进行复杂的操作时,可以方便的通过它们简化我们的操作。如:当我们写 babel 插件时,我们可以方便的利用 babel-types 中提供的方法进行操作,当然 babel-types 以及 node 节点都会作为入参传入到我们的 babel 插件的上下文中。

编写第一个 babel 插件

上图是在 astexplorer 的一个插件 demo。

export default function ({Plugin, types: t}) {
  return new Plugin('ast-transform', {
    visitor: {
      Identifier(node) {
        return t.identifier(node.name.split('').reverse().join(''));
      }
    }
  });
}

Identifier 方法中定义的一些操作就会在进入以及出 Identifier 节点时执行, 借助 babel-types 帮助函数返回一个新的 Identifier 节点。

以上是对 babel 整个工作流程的的了解,后面会对每一个部分做更进一步的了解。

@sundway sundway self-assigned this Sep 27, 2017
@sundway sundway added the 前端 label Sep 27, 2017
@sundway sundway changed the title 了解 Babel 了解 Babel (一) Oct 12, 2017
@sundway sundway changed the title 了解 Babel (一) 了解 Babel (一) Oct 12, 2017
@sundway sundway changed the title 了解 Babel (一) 了解 Babel Oct 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant