- 原文:关于Babel配置项的这点事
- 作者:tonyc726
Fundebug经授权转载,版权归原作者所有。
Babel作为一个JavaScript的语法编译器,可以将ES6/7/8代码转为ES5代码,从而在现有环境执行。
但是初次配置.babelrc的时候,各种presets、plugins看的眼花缭乱,不知道如何下手,下面就自己学习Babel时遇到的问题做一下总结:
如果你是初次接触babel,推荐阅读阮一峰的《Babel 入门教程》
Plugin、Preset、Stage-X的关系
按照Babel官网的介绍,其实Preset和Stage-X都是归属到Plugin里面的,只不过所覆盖的范围不同而已。
举个例子,如果需要转换ES2015(ES6)的语法,那么你可以在.babelrc的plugins中按需引入check-es2015-constants、es2015-arrow-functions、es2015-block-scoped-functions等等几十个不同作用的plugin:
// .babelrc
{
"plugins": [
"check-es2015-constants",
"es2015-arrow-functions",
"es2015-block-scoped-functions",
// ...
]
}
但是Babel团队为了方便,将同属ES2015的几十个Transform Plugins集合到babel-preset-es2015一个Preset中,这样你只需要在.babelrc的presets加入es2015一个配置就可以完成全部ES2015语法的支持了:
// .babelrc
{
"presets": [
"es2015"
]
}
另外,不论是Plugin还是Preset,有不少都有单独属于自己的配置项,具体如何操作的可以看一下官网的说明。
上面介绍了Plugin与Preset,那么Stage-X就很好理解了,stage-0、stage-1、stage-2、stage-3、分别对应的就是进入标准之前的5个阶段,不同stage-4stage-x之间存在依赖关系,数字越小,阶段越靠后,靠后阶段包含前面阶段所有的功能,简单理解就是stage-0包含stage-1/2/3的内容,所以如果你不知道需要哪个stage-x的话,直接引入stage-0就好了。
PS:
babel-preset-stage-4已经整合入Presets不单独发布了。
以上就是一些基础概念,目前,官方推荐使用babel-preset-env,它可以根据你的配置结合compat-table来帮你自动引入你需要的plugins,它有很多配置项,下面介绍几个常用的:
-
targets:
{ [string]: number | string },默认{};
需要支持的环境,可选例如:chrome, edge, firefox, safari, ie, ios, node,甚至可以指定版本,如node: "6.10"或者node: "current"代表使用当前的版本;
-
targets.node:
number | string | "current" | true;
指定node的版本,例如:6.10;
-
targets.browsers:
Array<string> | string;
指定需要兼容的浏览器清单,具体参考browserslist,例如:["last 2 versions", "safari >= 7"];
例如需要配置兼容["last 2 versions", "safari >= 7"]的babel-preset-env:
// .babelrc
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
此外,不同的plugins和presets或许有些功能是重复的,有些存在依赖关系,在配置的时候还有前后顺序的不同,那么Babel在运行的时候是怎么处理的呢?总结一下,规律大概有以下几点:
- plugins优先于presets进行编译;
- plugins按照数组的index增序(从数组第一个到最后一个)进行编译;
- presets按照数组的index倒序(从数组最后一个到第一个)进行编译,因为作者认为大部分会把presets写成
["es2015", "stage-0"],具体细节可以看这个。
babel-polyfill与babel-runtime的选择
Babel默认只转换新的JavaScript语法,而不转换新的API,比如Iterator、Generator、Set、Maps、Promise等等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码,具体的可以参考babel-plugin-transform-runtime模块的definitions.js文件。
babel-polyfill与babel-runtime就是为了解决这种全局对象或者全局对象方法不足的问题,而诞生的2种解决方式。
当然,你还可以用
promise-polyfill此类Polyfill解决全局对象的问题;
或者用lodash此类Utils解决Object.assign这种方法扩展的问题。
先说说babel-polyfill,它的做法比较暴力,就是将全局对象通通污染一遍,这样做的坏处有几点:
- 可能会增加很多根本没有用到的polyfill;
- 可能会污染子模块的局部作用域,严重的或许会导致冲突;
但是,这样做也有好处,如果你的运行环境比较low,比如说Android一些老机子,而你有需要大量使用Promise、Object.assign、Array.find之类的全局对象或者其所属方法,那么使用babel-polyfill,绝对是一劳永逸。
接着,再来说说babel-runtime,相对而言,它的处理方式比较温柔,套用步步高的广告词就是哪里需要加哪里,比如说你需要Promise,你只需要import Promise from 'babel-runtime/core-js/promise'即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。
不过,如果N个文件都需要Promise,难道得一个个文件的加import Promise from 'babel-runtime/core-js/promise'么,显然不是,Babel已经为这样情况考虑过了,只需要使用babel-plugin-transform-runtime就可以轻松的帮你省去手动import的痛苦,而且,它还做了公用方法的抽离,哪怕你有100个模块使用了Promise,但是promise的polyfill仅仅存在1份,所有要的地方都是引用一地方,具体的配置参考如下:
// .babelrc
{
"presets": [
"env",
"stage-0"
],
"plugins": [
"transform-runtime"
],
"comments": false
}
写在最后,我在Github上开了一个项目,做了几个测试,有兴趣的可以一起来试试看。
babel与webpack
关于babel与webpack结合使用的教程网上已经有很多了,有不少却还在用v1.*的版本(不推荐),从而在过渡到v2.*或者v3.*(推荐)的版本时,碰到一个关于babel的配置问题,示例如下:
// .babelrc - webpack v1.*
{
"presets": [
"env",
"stage-0"
]
}
// .babelrc - webpack v2.* - v3.*
{
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
很明显,一眼就能看出相对于v1.*的版本,v2.*或者v3.*版本多了"modules": false这项配置,如果仔细看官网的迁移指南,你就能明白了,以前你可能需要用babel来将ES6的模块语法转换为AMD、CommonJS、UMD之类的模块化标准语法,但是现在webpack已经把这个事情做了,所以就不需要babel来做了,但是babel配置项中的modules默认值是commonjs,所以你需要将modules设置为false才行,不然就冲突了。
参考资料
- Babel 入门教程
- 如何写好.babelrc?Babel的presets和plugins配置解析
- babel的polyfill和runtime的区别
- Babel 全家桶
- transform-runtime 会自动应用 polyfill,即便没有使用 babel-polyfill
- 如何区分Babel中的stage-0,stage-1,stage-2以及stage-3(一)
本文先发布于我的个人博客《Babel笔记》,后续如有更新,可以查看原文。











网友评论