关于Babel配置项的这点事

Babel做为一个JavaScript的语法编译器,能够将ES6/7/8代码转为ES5代码,从而在现有环境执行。html

可是初次配置.babelrc的时候,各类presetsplugins看的眼花缭乱,不知道如何下手,下面就本身学习Babel时遇到的问题作一下总结:node

若是你是初次接触babel,推荐阅读阮一峰的《 Babel 入门教程

Plugin、Preset、Stage-X的关系

按照Babel官网的介绍,其实PresetStage-X都是归属到Plugin里面的,只不过所覆盖的范围不一样而已。webpack

举个例子,若是须要转换ES2015(ES6)的语法,那么你能够在.babelrcplugins中按需引入check-es2015-constantses2015-arrow-functionses2015-block-scoped-functions等等几十个不一样做用的plugin:ios

// .babelrc
{
  "plugins": [
    "check-es2015-constants",
    "es2015-arrow-functions",
    "es2015-block-scoped-functions",
    // ...
  ]
}

可是Babel团队为了方便,将同属ES2015的几十个Transform Plugins集合到babel-preset-es2015一个Preset中,这样你只须要在.babelrcpresets加入es2015一个配置就能够完成所有ES2015语法的支持了:git

// .babelrc
{
  "presets": [
    "es2015"
  ]
}

另外,不管是Plugin仍是Preset,有很多都有单独属于本身的配置项,具体如何操做的能够看一下官网的说明github

上面介绍了Plugin与Preset,那么Stage-X就很好理解了,stage-0stage-1stage-2stage-3stage-4分别对应的就是进入标准以前的5个阶段,不一样stage-x之间存在依赖关系,数字越小,阶段越靠后,靠后阶段包含前面阶段全部的功能,简单理解就是stage-0包含stage-1/2/3的内容,因此若是你不知道须要哪一个stage-x的话,直接引入stage-0就行了。web

PS: babel-preset-stage-4已经整合入Presets不单独发布了。

以上就是一些基础概念,目前,官方推荐使用babel-preset-env,它能够根据你的配置结合compat-table来帮你自动引入你须要的plugins,它有不少配置项,下面介绍几个经常使用的:chrome

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

须要支持的环境,可选例如:chrome, edge, firefox, safari, ie, ios, node,甚至能够指定版本,如node: "6.10"或者node: "current"表明使用当前的版本;segmentfault

  • targets.nodenumber | string | "current" | true

指定node的版本,例如:6.10api

  • targets.browsersArray<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在运行的时候是怎么处理的呢?总结一下,规律大概有如下几点:

  1. plugins优先于presets进行编译;
  2. plugins按照数组的index增序(从数组第一个到最后一个)进行编译;
  3. presets按照数组的index倒序(从数组最后一个到第一个)进行编译,由于做者认为大部分会把presets写成["es2015", "stage-0"],具体细节能够看这个
摘自《 如何写好.babelrc?Babel的presets和plugins配置解析

babel-polyfillbabel-runtime的选择

Babel默认只转换新的JavaScript语法,而不转换新的API,好比IteratorGeneratorSetMapsPromise等等全局对象,以及一些定义在全局对象上的方法(好比Object.assign)都不会转码,具体的能够参考babel-plugin-transform-runtime模块的definitions.js文件。

babel-polyfillbabel-runtime就是为了解决这种全局对象或者全局对象方法不足的问题,而诞生的2种解决方式。

固然,你还能够用 promise-polyfill此类Polyfill解决全局对象的问题;
或者用 lodash此类Utils解决 Object.assign这种方法扩展的问题。

先说说babel-polyfill,它的作法比较暴力,就是将全局对象统统污染一遍,这样作的坏处有几点:

  1. 可能会增长不少根本没有用到的polyfill;
  2. 可能会污染子模块的局部做用域,严重的或许会致使冲突;

可是,这样作也有好处,若是你的运行环境比较low,好比说Android一些老机子,而你有须要大量使用PromiseObject.assignArray.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上开了一个项目,作了几个测试,有兴趣的能够一块儿来试试看。


2017.8.30 补充

关于babelwebpack结合使用的教程网上已经有不少了,有很多却还在用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的模块语法转换为AMDCommonJSUMD之类的模块化标准语法,可是如今webpack已经把这个事情作了,因此就不须要babel来作了,可是babel配置项中的modules默认值commonjs,因此你须要将modules设置为false才行,否则就冲突了。


参考资料
  1. http://www.ruanyifeng.com/blo...
  2. https://excaliburhan.com/post...
  3. https://segmentfault.com/q/10...
  4. https://github.com/brunoyang/...
  5. https://github.com/lmk123/blo...
  6. http://www.cnblogs.com/flying...
本文先发布于个人我的博客《 Babel笔记》,后续若有更新,能够查看原文。