首页
社区
课程
招聘
[原创] Babel 入门
发表于: 2022-12-5 07:31 7857

[原创] Babel 入门

2022-12-5 07:31
7857

Babel 是一个 JavaScript 编译器,它是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中,官网的链接是babel。也可以使用Babel插件以及工具来对js代码编译的过程进行修改,实现自定义js代码的生成。

Babel 工作流程

Babel处理js代码的主要的流程如下图所示,主要包含三个部分:解析(Parse)、转换(Transform)以及生成(Generate)。

 

babel-work-flow

 

三部分的主要功能是:

  • Parse:将js代码字符串解析成AST(抽象语法树,Abstract Syntax Tree),Babel中使用babel-parser工具来进行实现;
  • Transform:遍历Parse中生成的AST,并对AST进行相应的转换,从而实现对js代码的修改,Babel中使用babel-traverse来进行实现,Babel Traverse(遍历)模块维护了整棵树的状态,并且负责替换、移除和添加节点;
  • Generate:将上述经过Transform修改的AST代码,生成对应的js代码,最终实现js的转换,Bable中使用babel-generator来进行实现。

抽象语法树(AST)

在具体介绍Babel前,我们需要对抽象语法树有一定的理解,在计算机科学中,抽象语法树(Abstract Syntax Tree,AST)是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。简单来说,它是你所写代码的的树状结构化表现形式。

1
2
3
4
5
6
7
while(b !== 0){
  if(a > b){
      a -= b;
  }else{
      b -= a
  }
}

可以通过astexplorer网站来查看js代码对应生成的AST,如上的一段代码生成的AST如下所示生成的AST会比较长,就不直接贴网站上的代码了,可以自己贴上去生成。将对应的AST进行精简,形成如下图所示的树形结构。

 

ast-demo

 

可以通过BabelAST规范,来对Babel定义的AST中的每一个结点的每一个类型进行了解。

实践

通过编程来实现对js代码的修改与编译,因为没有想清楚要实现啥功能,所以此次只记录相应的过程要用到的接口以及经典的代码。

 

要先安装Bable对应的库:

1
npm install --save-dev @babel/core @babel/cli @babel/preset-env

第一步先是解析js代码形成AST,通过babel/parser进行实现,变量ast是对应得到的AST对象,如下所示:

1
2
3
4
5
6
7
8
9
const babylon = require('@babel/parser');
 
const code = require("fs").readFileSync("demo.js", "utf-8");
 
const ast = babylon.parse(code, {
  // parse in strict mode and allow module declarations
  sourceType: "script",
  filename: "test.js"
});

接下来是对AST树进行遍历,通过babel/traverse来实现:

1
2
3
4
5
6
7
8
9
10
const babelTraverse = require('@babel/traverse').default;
 
babelTraverse(ast, {
  enter(path) {
    ...
  },
  exit(path){
    ...
  }
});

babel/traverse会遍历AST上的所有结点,而访问每一个结点有两个时机:enterexitenter 是在节点中包含的子结点内容还没被解析时,而 exit 是在包含的子结点被解析完成之后。

 

可以通过下面的代码来输出ast结点的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const babelTraverse = require('@babel/traverse').default;
const babylon = require('@babel/parser');
 
const code = require("fs").readFileSync("src.js", "utf-8");
 
const ast = babylon.parse(code, {
  // parse in strict mode and allow module declarations
  sourceType: "script",
  filename: "test.js"
});
 
var indent = "";
 
babelTraverse(ast, {
  enter(path) {
    console.log(indent + "<" + path.node.type + ">");
    indent += "  ";
  },
  exit(path){
    indent = indent.slice(0, -2);
    console.log(indent + "<" + "/" + path.node.type + ">");
  }
});

大多数时候我们是不需要遍历和处理所有的结点,只需要处理某些特定结点(比如if语句)。这种情况下,babel/traverse提供了简单的visitor来过滤结点。可以在visitor中指定要处理的结点类型,从而实现筛选。下面的代码是只处理IfStatement

1
2
3
4
5
6
7
const myVisitor = {
  IfStatement(path) {
       ...
  }
};
 
babyTraverse(ast, myVisitor);

visitor也支持 enterexit

1
2
3
4
5
6
7
8
9
10
const myVisitor = {
  IfStatement: {
      enter(path){
          ...
      }
      exit(path){
          ...
      }
  }
};

接下来就是替换和修改了,在visitor中选中了要处理的结点后就可以对AST进行修改和更新:

1
2
3
4
5
6
7
8
9
10
babyTraverse(ast, {
  enter(path) {
    if (
      path.node.type === "Identifier" &&
      path.node.name === "n"
    ) {
      path.node.name = "x";
    }
  }
});

在处理完成之后,最终通过babel-generator来生成js代码:

1
2
3
4
5
const babelGenerator = require('@babel/generator').default;
 
generate(ast, {
    retainLines: false,
  }, code);

总结

很简单的了解了下babel,知道了原来通过ast也可以很简单的对js进行修改,本文只是一个入门的介绍,如果需要详细了解,可以看下面的参考。

参考

  1. babel
  2. Babel 插件手册
  3. Babel for ES6? And Beyond!
  4. Understanding ASTs by Building Your Own Babel Plugin
  5. Babel HelloWorld

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//