什么是Babel?
Babel是一套主要用来将使用ECMAScript2015+语法编写的代码转换成纯ES5的Javascript代码的工具,以兼容任何老式浏览器与运行环境。
Babel可以做什么?
Babel可以用来编译ES6+的语法,它使所有ES6+规范新增的语法糖都可用,包括:类(class),箭头函数(arrow function),多行字符串等等。它的功能包括但不限于:
1.Javascript语法转换;
2.使用ployfill特性使你的浏览器兼容各类全局web api(例如:promise,async await);
3.源代码转换;
Babel代码转换流程
@babel/core作为Babel的核心包,主要负责代码的转换工作。其内部使用了@babel/parser,@babel/traverse,@babel/generator三个工具来负责代码的转译过程。
parser:内部依赖acorn和acorn-jsx,将js代码解析为抽象语法树;
traverse:修改parser解析后的抽象语法树;
generator:解析traverse编辑后的抽象语法树,生成对应的es5语法;
Babel的代码转换其实是一个线性的流程,依次经历解析,转换,生成三个阶段。
Plugins和Presets
Plugin是Babel提供的用来帮助代码转换的工具,它主要在抽象语法树的转换阶段发挥作用,也就是traverse阶段。如果我们不使用任何plugins,源代码经过babel编译后会原样输出(不会保留原有代码格式)。Plugin则会告知babel如何转换我们的代码。
Babel提供了很多的plugins用来转换代码,其细粒度精确到每一种ECMAScript2015+语法,例如@babel/plugin-transform-arrow-functions负责转换箭头函数,@babel/plugin-proposal-object-rest-spread用来转换对象扩展运算符。同时babel的plugin还做了对框架语法的兼容,比如对react的jsx的转换。我们可以根据项目需求自行选定不同的插件。
上面提到了babel的plugins,plugins虽然功能丰富,但是es5+新出的语法还是比较多的,如果一个一个安装的话未免太麻烦,幸好babel提供了另外一种工具——presets。
如果把一个plugin比作一种工具,则presets则是工具箱。Presets是一组plugins的集合,用来转换一类语法。在实际项目中最常用preset便是@babel/preset-env了,它非常聪明可以帮助我们转换所有最新的Ecmascirpt标准语法。因此再也不需要去根据项目中使用到的语法而一个一个的安装插件,非常方便并且可以减小打包体积。
Polyfill
笔者曾经在开发的时候遇到过一个问题,webpack和babel都已经配置好,并且项目也可以在chrome中正常运行,但是在ie浏览器中却一直报错。找了一圈后来发现是项目中使用promise和async await的问题。于是我回头看了项目内babel配置,该用的插件也都用了,那么问题到底出在哪里呢?后来回顾了一个babel文档,才发现promise和async并不属于ES规范语法,而是全局api。
Polyfill就是babel提供的可以帮助我们兼容这些内建api的工具。这意味着您可以使用新的内置函数(例如Promise或WeakMap),静态方法(例如Array.from或Object.assign),实例方法(例如Array.prototype.includes)和生成器函数(前提是您使用了regenerator插件)。 为了做到这一点,polyfill将诸如String之类的本地原型添加到了全局范围。
Babel常用配置
Babel的配置可以说是非常简单了。Babel官方提供了两种配置文件以供选择:
babel.config.js: 使用编程的方式来进行配置;
module.exports = function (api) {
api.cache(true);
const presets = [ ... ];
const plugins = [ ... ];
return {
presets,
plugins
};
.babelrc:静态配置,仅适用于单个程序包;
{
"presets": [...],
"plugins": [...]
}
如果项目中使用了webpack,则需要在webpack配置文件内配置babel-loader以启用babel配置
//webpack.config.js
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
}
]
},
//.babelrc
{
"presets": [
["@babel/preset-env"],
["@babel/preset-react"]
]
}
或者
//webpack.config.js
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', "@babel/preset-react"],
}
}
}
]
},
(此种配置方式无需配置babel配置文件)
如果项目中需要用到polyfill,可以将其在项目入口文件顶部引入或者加入到webpack配置文件的入口配置:
//index.js
require('@babel/polyfill');
or
import '@babel/poltfill';
//webpack.config.js
entry: ['@babel/polyfill', './index.js'],
注意在babel7.4.0之后的版本polyfill将会被弃用,建议您直接使用polyfill包含的core-js/stable和regenerator-runtime/runtime。
//index.js
import "core-js/stable";
import "regenerator-runtime/runtime";
如果您觉得直接引入core-js会增大打包体积,也可以使用按需引入的方式:
//index.js
import 'core-js/features/promise';
import 'core-js/features/array/from';