看了两个webpack2基础的视频教程, 发现两位老师所用的babel-preset的差异, 于是查了一下两者的区别, 看到了一篇相关的文章, 尝试将其翻译. 原文: babel-preset-env: a preset that configures Babel for you.

使用babel-preset-env, 可以另外声明环境, 然后该preset根据你的声明自动激活必要的插件.

存在的问题

现在, 有多个preset让我们选择, 以决定Babel应该支持编译具有哪一些特性的代码:

  • babel-preset-es2015, babel-preset-es2016等: 增量支持不同版本的ECMAScript. babel-preset-es2015将含有ES6新特性的代码编译为ES5下的代码, babel-preset-es2016则将含有ES2016新特性的代码编译为ES6下的代码.

  • babel-preset-latest: 支持编译所有ECMAScript版本下特性或处于stage4阶段的特性的代码(实际上两者是差不多的).

这些preset的问题就是: 它们都太”重”了, 即包含了过多在某些情况下不需要的功能. 比如, 现代的浏览器大多支持ES6的generator, 但是如果你使用babel-preset-es2015, 它会将generator函数编译为复杂的ES5代码, 这是没有必要的.

解决方案

babel-preset-envbabel-preset-latest的功能很相似, 但使用babel-preset-env, 我们可以声明环境, 然后该preset就会只编译包含我们所声明环境缺少的特性的代码.

要注意, 这就意味着我们需要自己另外去安装激活某些插件或preset以处理某些实验性特性(这些特性往往不是babel-preset-latest的一部分).

使用babel-preset-env的好处是: 你不再需要es20xx preset.

浏览器

对于特定的浏览器, 你也可以添加相关的配置:

  • 根据browserslist对浏览器相关信息执行配置. 例如:

    • 支持大部分浏览器最新的两个版本以及IE 7+:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    "babel": {
    "presets": [
    [
    "env",
    {
    "targets": {
    "browsers": ["last 2 versions", "ie >= 7"]
    }
    }
    ]
    ]
    },
    • 支持超过市场份额5%的浏览器:
    1
    2
    3
    "targets": {
    "browsers": "> 5%"
    }
  • 某个固定版本的浏览器:

    1
    2
    3
    "targets": {
    "chrome": 56
    }

Node.js

如果你想要使用Babel实时编译Node的代码, babel-preset-env就特别有用, 因为它支持在配置中将Node的版本声明为当前版本, 这样每一次都会对Node的最新版本执行编译.

1
2
3
4
5
6
7
8
9
10
11
12
"babel": {
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
]
]
},

想看实例可以查看这个GitHub repo: async-iter-demo

配置babel-preset-env

这部分简单介绍babel-preset-env的配置选项, 更多细节详见该preset的README文件.

模块系统(字符串, 默认为”common.js”)

配置你想要将ES6模块编译为哪一种模块系统:

  • 编译为其他流行的模块系统: “amd”, “commonjs”, “systemjs”, “umd”

  • 不编译, 还是使用ES6模块系统, 则将值声明为false.

include, exclude(数组, 默认为”[ ]”)

  • 在include中声明的值一般是元素为插件名的数组, 表示在项目中使用这些插件(如改写有缺陷的原生特性的插件), 在include中一起声明和分别另外声明插件的效果是一样的.

  • 在exclude中声明的值也是元素为插件名的数组, 但表示的是在项目中不可以使用这些插件.

useBuiltIns(布尔值, 默认为false)

Babel标准库本身含有针对新特性的polyfill. 使用babel-preset-env可以配置是否需要某一部分的polyfill.

使用polyfill有两种方式:

  • 使用core-js polyfills(动词) ES5, ES6+的代码

    • 安装该polyfill: npm install core-js --save
    • 激活该polyfill: import "core-js";
  • 使用babel-polyfill polyfills core-js和regenetator runtime(在ES5中模拟generator)

    • 安装该polyfill: npm install babel-polyfill --save
    • 激活: import "babel-polyfill";

两个中任何一个import声明都会被编译为针对特定环境的几个更细节的按顺序引入的import声明. 例如:

1
2
3
4
5
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
import "core-js/modules/web.timers";
import "core-js/modules/web.immediate";
import "core-js/modules/web.dom.iterable";

需要注意的几点:

  • 要确保某个需要的polyfill在项目中被激活一次, 比如在”main”模块中
  • 使用useBuiltIns后, 浏览器下载的代码量就会变少(bundle.js文件更小). However, it does not save RAM, because the polyfill only installs what is missing.

要了解关于标准库中polyfill的更多信息, 可以查看”Setting up ES6”中的”Babel: configuring standard library and helpers“章节.

调试(布尔值, 默认为false)

通过console.log()输出以下信息:

  • 目标环境
  • 使用的的转换规则
  • 使用的插件
  • 使用的polyfill

示例详见下一节.

示例

以下例子从babel-preset-env的README文件中截取:

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
[ "env", {
"targets": {
"safari": 10
},
"modules": false,
"useBuiltIns": true,
"debug": true
}]
]
}

在以上示例中, 设置了不编译模块, 我们可以使用webpack来处理import与export.

调试的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Using targets:
{
"safari": 10
}

Modules transform: false

Using plugins:
transform-exponentiation-operator {}
transform-async-to-generator {}

Using polyfills:
es7.object.values {}
es7.object.entries {}
es7.object.get-own-property-descriptors {}
web.timers {}
web.immediate {}
web.dom.iterable {}

babel-preset-env如何获取并判断配置信息

接下来要实现的功能

给予插件获取”环境”信息的权限

接下来的计划是给予插件检视在当前”环境”下可以实现哪些功能的能力. 这将带来两个好处:

  • 某些插件(比如针对对象的扩展运算符插件)现在需要对其配置决定是使用原生的功能还是polyfill. 如果该插件可以获取环境信息, 就不再需要手动进行配置.

  • 基于Babel的压缩插件有能力决定是否要对某些特性执行转换(如箭头函数).

简化preset

  • babel-preset-env中不再支持大多数基于ECMAScript版本的preset(如babel-preset-es2015, babel-preset-es2016). Babel团队现在正考虑在将来的Babel release中淘汰这些preset(比如通过弃用的方式(deprecation process))

  • 基于TC39进程(如stage-3等)的preset也因stage的不稳定性即将被淘汰, 某个提案的stage很有可能在2个月就发生变化. 因此, 直接通过插件来添加某些实验性特性是个更好的方法.

扩展阅读


参考