-
-
[原创] Babel 入门
-
发表于: 2022-12-5 07:31 8194
-
Babel 是一个 JavaScript 编译器,它是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中,官网的链接是babel。也可以使用Babel插件以及工具来对js代码编译的过程进行修改,实现自定义js代码的生成。
Babel 工作流程
Babel处理js代码的主要的流程如下图所示,主要包含三个部分:解析(Parse)、转换(Transform)以及生成(Generate)。

三部分的主要功能是:
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进行精简,形成如下图所示的树形结构。

可以通过Babel的AST规范,来对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上的所有结点,而访问每一个结点有两个时机:enter 和 exit。enter 是在节点中包含的子结点内容还没被解析时,而 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也支持 enter 和 exit:
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进行修改,本文只是一个入门的介绍,如果需要详细了解,可以看下面的参考。
参考
- babel
- Babel 插件手册
- Babel for ES6? And Beyond!
- Understanding ASTs by Building Your Own Babel Plugin
- Babel HelloWorld