.babelrc配置详解

1,520 阅读9分钟

什么是Babel

The compiler for writing next generation JavaScript.

官网是这么说的,翻译一下就是下一代JavaScript 语法的编译器。

作为前端开发,由于浏览器的版本和兼容性问题,很多JavaScript的新的方法都不能使用,等到可以大胆使用的时候,可能已经过去了好几年。

Babel就因此而生,它可以让你放心使用大部分的JavaScript的新的标准的方法,然后编译成兼容绝大多数的主流浏览器的代码。

在升级到了Babel6.x版本之后,所有的插件都是可插拔的。这也意味着你安装了Babel之后,是不能工作的,需要配置对应的.babelrc文件才能发挥完整的作用。

Babel编译器

在Babel执行编译的过程中,会从项目的根目录下的 .babelrc文件中读取配置。

.babelrc是一个json格式的文件。

在.babelrc配置文件中,主要是对预设(presets) 和 插件(plugins) 进行配置。

.babelrc配置文件一般为如下:

{
  "plugins": [
     [
      "transform-runtime",
      {
        "polyfill": false
      }
     ]
   ],
   "presets": [
     [
       "env",
       {
         "modules": false
       }
     ],
     "stage-2",
     "react"
  ]
}

plugins

该属性是告诉babel要使用那些插件,这些插件可以控制如何转换代码。

理解 babel-polyfill 和 babel-runtime及babel-plugin-transform-runtime

Babel默认只转换新的javascript语法,而不转换新的API,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象。以及一些在全局对象上的方法(比如 Object.assign)都不会转码。

比如说,ES6在Array对象上新增了Array.form方法,Babel就不会转码这个方法,如果想让这个方法运行,必须使用 babel-polyfill来转换等。

因此:babel-polyfill和babel-runtime就是为了解决新的API与这种全局对象或全局对象方法不足的问题。

那么他们两者的区别是什么?

babel-polyfill 的原理是当运行环境中并没有实现的一些方法,babel-polyfill会做兼容。
babel-runtime 它是将es6编译成es5去执行。我们使用es6的语法来编写,最终会通过babel
-runtime编译成es5.也就是说,不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码
成ES5.所以就有很多冗余的代码。

babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运
行环境中不支持Array.prototype.find 方法,引入polyfill, 我们就可以使用es6方法来
编写了,但是缺点就是会造成全局空间污染。

babel-runtime: 它不会污染全局对象和内置对象的原型,比如说我们需要Promise,我们只
需要import Promise from 'babel-runtime/core-js/promise'即可,这样不仅避免
污染全局对象,而且可以减少不必要的代码。

虽然 babel-runtime 可以解决 babel-polyfill中的避免污染全局对象,但是它自己也有 缺点的。

比如上,如果我现在有100个文件甚至更多的话,难道我们需要一个个文件加import Promise from 'babel-runtime/core-js/promise' 吗?

那这样肯定是不行的,因此这个 时候出来一个 叫 babel-plugin-transform-runtime, 它就可以帮助我们去避免手动引入 import的痛苦,并且它还做了公用方法的抽离。

比如说我们有100个模块都使用promise,但是promise的polyfill仅仅存在1份。这就是 babel-plugin-transform-runtime 插件的作用。

理解 babel-plugin-transform-runtime 的配置一些选项

因此通过上面的理解,我们可以对 transform-runtime 通过如下配置 plugins; 如下代码:

{
  'plugins': [
    [
      'transform-runtime', 
      {
        'helpers': false,
        'polyfill': false,
        'regenerator': true,
        'moduleName': 'babel-runtime'
      }
    ]
  ]
}

helpers: 默认值为true,表示是否开启内联的babel helpers(即babel或者环境本来存在的某些对象方法函数)

如:extends,etc这样的在调用模块名字时将被替换名字。

polyfill:默认值为true,表示是否把内置的东西(Promise, Set, Map)等转换成非全局污染的。

regenerator:默认值为true,是否开启generator函数转换成使用regenerator runtime来避免污染全局域。

moduleName:默认值为 babel-runtime,当调用辅助 设置模块(module)名字/路径. 比如如下这样设置:

{
  "moduleName": "flavortown/runtime"
}

import引入文件如下这个样子:

import extends from 'flavortown/runtime/helpers/extends';

transform-remove-console

{  "plugins": ["transform-remove-console"]}

使用这个插件,编译后的代码都会移除console.*,妈妈再也不用担心线上代码有多余的console.log了。

当然很多时候,我们如果使用webpack,会在webpack中配置。

这也告诉我们,Babel不仅仅是编译代码的工具,还能对代码进行压缩,也许有一天,你不再需要代码压缩的插件了,因为你有了Babel!

presets

presets属性告诉Babel要转换的源码使用了哪些新的语法特性,presets是一组Plugins的集合。

理解 babel-preset-env

比如:

babel-preset-es2015: 可以将es6的代码编译成es5.
babel-preset-es2016: 可以将es7的代码编译为es6.
babel-preset-es2017: 可以将es8的代码编译为es7.
babel-preset-latest: 支持现有所有ECMAScript版本的新特性。

举个列子,比如我们需要转换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"
  ]
}

但是我们随着时间的推移,将来可能会有跟多的版本插件,比如 bebel-preset-es2018,.... 等等。

因此 babel-preset-env 出现了,它的功能类似于 babel-preset-latest,它会根据目标环境选择不支持的新特性来转译。

首先需要在项目中安装,如下命令:

npm install babel-preset-env --save-dev

在.babelrc配置文件中,可以如下简单的配置:

{
  "presets": ['env']
}

我们还可以仅仅配置项目所支持的浏览器的配置

支持每个浏览器最后两个版本和safari大于等于7版本所需的polyfill代码转换,我们可以如下配置:

{
  'presets': [
    ['env', {
      'target': {
        'browsers': ['last 2 versions', 'safari >= 7']
      }
    }]
  ]
}

支持市场份额超过5%的浏览器,可以如下配置:

{
  'presets': [
    ['env', {
      'target': {
        'browsers': '> 5%'
      }
    }]
  ]
}

指定浏览器版本,可以如下配置:

{
  'presets': [
    ['env', {
      'target': {
        'chrome': 56
      }
    }]
  ]
}

Node.js如果通过Babel编译Node.js代码的话,可以设置 "target.node" 是 'current', 含义是支持的是当前运行版本的nodejs。

{
  "presets": [
    ["env", {
      "targets": {
        "node": "current"
      }
    }]
  ]
}

babel-preset-env 中的选项配置:

targets: {[string]: number | string }, 默认为{};

含义是支持一个运行环境的对象,比如支持node版本;可以如下配置: node: '6.0'; 运行环境: chrome, opera, edge, firefox, safari, ie, ios, android, node, electron

targets.browsers <Array | string>

支持浏览器的配置项,该配置项使用方式可以到 browserslist来查询。 比如上面的支持每个浏览器最后两个版本和safari大于等于7版本。如上配置。

modules

该参数的含义是:启用将ES6模块语法转换为另一种模块类型。将该设置为false就不会转换模块。默认为 'commonjs'. 该值可以有如下: 'amd' | 'umd' | 'systemjs' | 'commonjs' | false

我们在项目中一般会看到如下配置,设置modules: false, 如下代码配置:

"presets": [
   'env',
   {
     'modules': false
   }
]

这样做的目的是:以前我们需要使用babel来将ES6的模块语法转换为AMD, CommonJS,UMD之类的模块化标准语法,但是现在webpack都帮我做了这件事了,所以我们不需要babel来做,因此需要在babel配置项中设置modules为false,因为它默认值是commonjs, 否则的话,会产生冲突。

loose, 该参数值默认为false。

含义是:允许它们为这个 preset 的任何插件启用”loose” 转换。

include: 包含一些插件,默认为 [];

比如包含箭头函数,可以如下配置:

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      },
      "include": ["transform-es2015-arrow-functions", "es6.map"]
    }]
  ]
}

exclude; 排除哪些插件,默认为 [];

比如 排除生成器,可以如下配置:

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      },
      "exclude": ["transform-regenerator", "es6.set"]
    }]
  ]
}

理解 babel-presets-stage-x

官方预设(preset), 有两种,一个是按年份(babel-preset-es2017),一个是按阶段(babel-preset-stage-0)。

这主要是根据TC39 委员会ECMASCRPIT 发布流程来制定的。因此到目前为止 有4个不同的阶段预设:

babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3

以上每种预设都依赖于紧随的后期阶段预设,数字越小,阶段越靠后,存在依赖关系。也就是说stage-0是包括stage-1的,以此类推。因此 stage-0包含stage-1/2/3的内容。

所以如果我们不知道需要哪个stage-x的话,直接引入stage-0就好了。

stage0: 只是一个美好激进的想法,一些 Babel 插件实现了对这些特性的支持 ,但是不确定是否会被定为标准.

stage1: 值得被纳入标准的特性.

stage2: 该特性规范己经被起草,将会被纳入标准里.

stage3: 该特性规范已经定稿,大浏览器厂商和 Node.js 社区己开始着手实现.

但是在我们使用的时候只需要安装你想要的阶段就可以了:

比如 babel-preset-stage-2, 安装命令如下:

npm install --save-dev babel-preset-stage-2

自定义预设或插件

Babel支持自定义的预设(presets)或插件(plugins)。

如果你的插件在npm上,可以直接采用这种方式"plugins": ["babel-plugin-myPlugin"],当然,你也可以缩写,它和"plugins": ["myPlugin"]是等价的。

此外,你还可以采用本地的相对路径引入插件,比如

"plugins":["./node_modules/asdf/plugin"]

presets同理。

plugins/presets排序

具体而言,plugins优先于presets进行编译。

plugins按照数组的index增序(从数组第一个到最后一个)进行编译。 presets按照数组的index倒序(从数组最后一个到第一个)进行编译。

{
    "presets": ["es2015","stage-0"],
    "plugins": ["transform-runtime"]
}

这是比较推荐的配置(真的是太简单了,还花了这么久去看文档)。

强烈推荐使用transform-runtime。

当然,如果你的项目需要react或者flow这些语法的编译,请在presets里加入对应的值即可。

如果你需要非实例方法"foobar".includes("foo")之类的方法,按需引入babel-polyfill。