-
-
[原创] Babel 入门
-
发表于: 2022-12-5 07:31 7886
-
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