Babel
babel是让javascript中的es6/es7等中的新语法转换为es5,让低端运行环境能够认识并执行。
Babel工作流程:
babel主要有三个处理步骤:解析(parse)、转换(transform)、生成(generate)。
- 解析
将代码解析成抽象语法树(AST),每个js引擎都有自己的AST解析器,而babel是通过Babellon实现的。在这个解析过程中有两个阶段:词法分析和语法分析。词法分析阶段把字符串形式的代码转换为令牌流,令牌类似于AST中节点;而语法分析阶段则是把一个个令牌流转为AST的形式,同时这个阶段会把令牌中的信息转为AST的表述结构。
- 转换
在这个阶段,babel接收得到AST并通过babel-traverse对其进行深度优先遍历,在此过程中对节点进行添加、更新和移除操作。
- 生成
将经过转换的AST通过babel-generator再转为js代码,过程就是深度优先遍历整个AST,然后构建可以表示转换后代码的字符串。
总结:首先我们编写的代码在编译阶段解析成抽象语法树,然后经过一系列的遍历和转换,然后再将转换后的抽象语法树生成常规的js代码。
babel其他配套工具
(1)babel-cli
命令行工具,安装了babel-cli就能够在命令行中使用babel命令来编译文件。
(2)babel-polyfill
babel默认只转换js语法,而不转换新的API,比如Iterator、Generator、Set、Map、promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign())都不会转码。
比如es6在Array对象上新增了Array.from方法,babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill。
babel-polyfill主要有两个缺点:
使用babel-ployfill打包出来会非常大,因为babel-loader是一个整体,把所有方法都加到了原型链上。这样会造成浪费,这个问题可以通过单独使用core.js的某个类库来解决。
babel-polyfill会污染全局变量,给很多类的原型链上都做了修改。
(3)babel-runtime和babel-plugin-transform-runtime
把帮助类方法从每次使用前定义改为统一require,精简代码,babel-runtime需要安装为依赖,而不是开发依赖。
(4)babel-loader
使用webpack时通过babel-loader进行转换。babel-loader只会对es6的语法进行解析,例如箭头函数,但如果要解析es6的API方法就要引入babel-polyfill或者babel-runtime。
babel-polyfill与babel-runtime的区别:
比如浏览器不能识别async/await,可以通过babel-polyfill在全局进行定义,但是这样会造成全局变量污染,所以更好的办法是采用babel-runtime可以实现局部定义。
例如我们在项目中使用promise
import Promise from "babel-runtime/core-js/promise"; let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!');
项目中只使用了promise,所以只需要引入promise的帮助函数即可,但是当项目文件较多时,使用es6API也较多时,手动引入就变得很繁琐,这时就需要transform-runtime插件了,该插件会分析项目代码,自动引入所需要的垫片API。
抽象语法树(AST)
抽象语法树是源代码的抽象语法结构的树状表示。树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,是因为抽象语法树并不会表示出真实语法中出现的每个细节。比如嵌套括号被隐藏在树的结构中,并没有以节点的形式呈现。
抽象语法树应用在很多领域:比如浏览器、编译器等。
抽象语法树的作用:因为编程语言太多,需要统一的结构让计算机识别。