一直以来都对babel-runtimebabel-preset-envbabel-plugin-transform-runtimebabel-polyfill等库之间的关系,所以就去仔细研究下喽。

先简单介绍一下Babel

它是一个编译器可以让你使用最新版本的ES规范比如ES2015(ES6)ES2016(ES7)ES2017(ES8)的写法并把它编译成老的ES5的写法。

babel-core 是 babel 的编译器核心。

意思即你可以使用最新的JavaScript规范的写法比如async等来写代码,然后Babel会帮你编译成ES5以兼容老旧的浏览器。

转换前:

1
2
3
[1, 3, 5].reduce((accumulator, currentValue) => {
return accumulator + currentValue;
});

转换后:

1
2
3
[1, 3, 5].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
});

文章中凡是以@babel开头的都是指的Babel 7.x的否则是Babel 6.x,@的意思可参考npm 官方,标星号的表示只有 @babel 版本的才会有的参数。

安装

可以选择全局或者本地安装,最好是依据项目本地安装。

1
npm i -D babel-cli

在项目根目录下创建.babelrc文件来配置Babel或者是在package.json里面进行设置。

命令

babel script.js --out-file script-compiled.js babel来编译输出

babel-node是一个和babel一样功能的命令,但是它有缓存功能并且会编译 ES6 代码后再运行它。

babel-polyfil

根据官网的表述由于Babel只是转换了语法比如箭头函数,但是并没有实现兼容那些新的原生方法比如PromiseArray.reduce所以必须使用。它使用了core-jsregenerator

必须安装为依赖npm i babel-polyfill
需要注意的是这个有一个弊端会污染全局的变量,后面会有解决方案。

babel-preset-env

npm i -D babel-preset-env当不进行任何设置的时候和babel-preset-latest一样就是默认使用 ES2015(ES6)ES2016(ES7)ES2017(ES8)的新语法。

1
2
3
{
"presets": ["env"]
}

参数

这里有一个参数useBuiltIns,这个参数默认值为false意思是不为每个文件自动添加兼容插件,或者把 import "@babel/polyfill"根据不同目标环境拆分为多个的兼容插件。

比如当启用的时候:

输入:

1
import "@babel/polyfill";

输出依据不同的输出环境:

1
2
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";

这里的输出环境即目标环境比如下 .babelrc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 2 versions"
]
}
}
]
],
}

这里需要注意的是只有2.x版本的useBuiltIns参数才会有usageentry选项,参见这里

useBuiltInsbabel 6版本只有两个值truefalse。默认false表示不自动为每个文件添加兼容插件或者把对import "@babel/polyfill"的引用依据不同的目标环境输出不同的兼容插件。

Babel 的插件有两种模式:

  • 正常模式,尽量贴近 ES6 的书写语法。
  • 更类似 ES5 的代码

loose选项大概的意思即编译出来的代码更像ES5而不是ES6的语法。
优缺点如下:

  • 编译出来的代码在旧的引擎中可能会运行得更快和兼容
  • 缺点是在以后的版本中当从编译的ES6切换到原生ES6可能会有问题,不过这种情况极少。

例如如下代码:

1
2
3
4
5
6
7
8
9
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}

注意看如下行(A)那里的不同,一个是使用ES6语法一个是使用ES5。
正常模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
"use strict";
var _createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor); // (A)
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Point = (function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
_createClass(Point, [{
key: "toString",
value: function toString() {
return "(" + this.x + ", " + this.y + ")";
}
}]);
return Point;
})();

宽松模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"use strict";
function _classCallCheck(instance, Constructor) { ··· }
var Point = (function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
Point.prototype.toString = function toString() { // (A)
return "(" + this.x + ", " + this.y + ")";
};
return Point;
})();

具体可参见这里

插件

Babel是一个编译器,在高级层面它分三阶段解析,转换,生成代码。

官方 Presets

如果不想自己设置一堆插件的话,官方有envreactflow三个 Presets。即预安装了 plugins 的配置。

Stage-X(试验性 Presets)

stage-x presets指的是任何未被包括在官方发布版比如(ES6/ES2015)中的草案。任何 stage-3 之前的都应该要谨慎使用,
一共有以下几个试验性的版本:

  • Stage 0 - 草稿:只是一个设想可能是一个 Babel 插件
  • Stage 1 - 提案:值得去推进的东西
  • Stage 2 - 草案:初始化规范
  • Stage 3 - 候选:完整规范和初始化浏览器实现
  • Stage 4 - 完成:会加入到下一版中

转换插件

有很多的转换插件具体可查看这里

语法插件

这种插件可以让 Babel 来解析特殊类型的语法。

如果对应的转换插件已经使用了转换插件会自动使用语法插件。

1
2
3
4
5
{
"parserOpts": {
"plugins": ["jsx", "flow"]
}
}

Plugins/Preset 路径

如果插件发布到npm上面则可以使用"plugins": ["babel-plugin-myPlugin"],也可以使用"plugins:": ["./node_modules/pluginPath"]

Plugins/Preset 快捷方式

如果插件前缀是babel-plugin-则可以使用"plugins:": ["myPlugin"]presets同理
"presets": ["@org/babel-preset-name"]。作用域包也是如此,"presets": ["@org/babel-preset-name"] 快捷方式为:"presets": ["@org/name"]

Plugin/Preset 顺序

当这两种插件同时处理代码的时候,将以插件或者preset的顺序来进行转换。

  • 插件在presets之前运行
  • 插件顺序是从第一到最后
  • Preset顺序是相反的从最后到第一

比如:

1
2
3
4
5
6
{
"plugins": [
"transform-decorators-legacy",
"transform-class-properties"
]
}

将会先运行transform-decorators-legacytransform-class-properties

Presets的顺序是相反的:

1
2
3
4
5
6
7
{
"presets": [
"es2015",
"react",
"stage-2"
]
}

将会以stage-2reactes2015的顺序运行。这主要是因为向后兼容性,因为大多数用户把es2015 放在 stage-0 之前。

Plugins 和 Preset 参数

1
2
3
4
5
6
7
8
{
"plugins": [
["transform-async-to-module-method", {
"module": "bluebird",
"method": "coroutine"
}]
]
}
1
2
3
4
5
6
7
8
{
"presets": [
["es2015", {
"loose": true,
"modules": false
}]
]
}

开发插件

参考babel-handbook

1
2
3
4
5
6
7
8
9
10
11
export default function () {
return {
visitor: {
Identifier(path) {
const name = path.node.name;
// reverse the name: JavaScript -> tpircSavaJ
path.node.name = name.split("").reverse().join("");
}
}
};
}

开发 Preset

1
2
3
4
5
6
7
8
9
10
// Presets can contain other presets, and plugins with options.
module.exports = {
presets: [
require("babel-preset-es2015"),
],
plugins: [
[require("babel-plugin-transform-es2015-template-literals"), { spec: true }],
require("babel-plugin-transform-es3-member-expression-literals"),
],
};

查看es2015 preset

Plugins 和 Preset 配合使用

如果Plugins和Preset配合使用的话假设如下配置:

1
2
3
4
5
6
7
8
9
10
11
{
"plugins": ["@babel/plugin-transform-for-of"],
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": ["last 2 versions", "not ie <= 10"],
},
"debug": true,
}],
],
}

以上 @babel 指的是在babel组织下的相关包这里即是兼容的babel 7.x 版本。按照解析的规则是 plugins先于presets,之后再进入presets,转化出来的代码是一样的,因为@babel/preset-env即是兼容了那个es6for...of 写法。

babel-runtime 和 babel-plugin-transform-runtime

先来介绍 babel-plugin-transform-runtime。

需要注意的是实例方法例如"foobar".includes("foo")因为会更改内置的方法所以不会正常工作需要引用babel-polyfill

这个插件的作用有两个:

  • 由于Babel使用工具函数作为常用函数比如_extend。默认情况下会被添加进每个文件中。这样就会导致重复,特别是当程序包含了多个文件。所有的工具函数都会引用模块babel-runtime来减少代码重复,运行时会被编译进构建的代码之中。

  • 另一个作用是代码提供一个沙箱环境。当你使用babel-polyfill和其内置的诸如 Promise, Set 等新 API,这些会污染全局变量。当是一个 app 的时候或者是一个命令行工具的时候不会有问题,但是如果是想要发布来给别人用的时候就会因为无法控制目标环境而出现问题。

这个转换插件将对这些内置函数的引用指向core-js这样就可以不需要使用 polyfill。
一般情况下需要安装babel-plugin-transform-runtime来作为一个开发依赖包。

1
npm i -D babel-plugin-transform-runtime

然后安装babel-runtime来作为生产环境的依赖包。

1
npm i babel-runtime

转换插件只是在开发环境使用,但是运行时插件依赖于部署发布的代码。
在 .babelrc 中使用如下配置即可:

1
2
3
4
5
6
7
8
9
10
{
"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}]
]
}

参数

  • helpers

    默认为 true,是否内联的 Babel 工具函数替换为对模块的引用。
    即以下代码:

    1
    class Person {}

一般会转换为:

1
2
3
4
5
6
7
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Person = function Person() {
_classCallCheck(this, Person);
};

使用 babel-runtime 转换插件会转换为:

1
2
3
4
5
6
7
8
9
10
11
"use strict";
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Person = function Person() {
(0, _classCallCheck3.default)(this, Person);
};

这里在实际操作过程中需要添加类似如下的代码否则是不会进行任何转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"plugins": [
["@babel/plugin-transform-runtime", {
"moduleName": "@babel/runtime"
}]
],
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 2 versions"
]
}
}
]
],
}
  • polyfill

    默认 true,是否允许让内置的诸如PromiseSetMap使用不会污染全局作用域的兼容插件。

  • regenerator

    默认为 true,是否设置生成器函数是否使用再生器运行时插件来避免污染全局作用域。

  • moduleName

    默认为"babel-runtime"。当使用工具函数的时候设置模块的名称 / 路径。

  • useBuiltIn*

    默认为 false。当启用的意思即当使用工具函数的时候不再引用core-js中的兼容插件。

  • useESModules*

    默认 false,当启用的时候转换插件就不会使用经过 @babel/plugin-transform-modules-commonjs 处理的工具函数。这样有利于为诸如 webpack 的模块构建系统减少构建包的大小,因为 webpack 不需要保存 commonjs 的语法。

babel-plugin-transform-runtime,babel-preset-env,babel-polyfill 的使用

这三个插件是如何配合的?这里主要是因为 Babel 7 和 Babel 6,会造成一些区别,分情况来说明吧。

举个例子吧,目录结构如下:

1
2
3
4
5
6
7
8
9
10
|-----build
| |
| |----test.js
| |----test-compiled.js
|
|-----.babelrc
|
|-----package.json
|
|-----webpack.config.js

执行如下命令

1
2
3
4
5
6
7
mkdir babel-learn
npm init -y
npm i -D @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env webpack@3.7.1
npm i @babel/polyfill @babel/runtime
  • Babel 7

    babel-preset-env 需要和 babel-polyfill 配合使用。例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": [
    "last 2 versions"
    ]
    },
    "useBuiltIns": "usage",
    "debug": true
    }
    ]
    ],
    }

    package.json 中添加

    1
    "babel": "babel build/test.js --out-file build/test-compiled.js"

    运行 npm run babel 即可。

    例一

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": [
    "last 2 versions"
    ]
    },
    "useBuiltIns": "usage",
    "debug": true
    }
    ]
    ],
    }

    test.js 内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    "use strict";
    require("core-js/modules/es6.promise");
    function _classCallCheck(instance, Constructor) { if (!(instance instanceofConstructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    可以看到上面是把 test.js 在目标浏览器环境下是通过引用 polyfill 的方式。

    1
    require("core-js/modules/es6.promise")

    这里如果不想要用 commonjs 的方式引入兼容的插件可以设置 modules 参数。

    例二

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": "entry",
    "debug": true
    }
    ]
    ],
    }

    test.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import "@babel/polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    "use strict";
    require("core-js/modules/es6.array.sort");
    require("core-js/modules/es6.date.to-json");
    require("core-js/modules/es6.typed.array-buffer");
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例三

    当设置 useBuiltIns 为 false 的时候的输出。
    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import "@babel/polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    "use strict";
    require("@babel/polyfill");
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例四

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    {
    "plugins": [
    ["@babel/plugin-transform-runtime", {
    "moduleName": "@babel/runtime"
    }]
    ],
    }

    test.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var _Promise = require("@babel/runtime/core-js/promise");
    class Person {}
    let a = new _Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    可以看到上面的 Promise 并没有污染全局的 Promise 而是引用了 @babel/runtime/core-js/promise。

    例五

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "plugins": [
    ["@babel/plugin-transform-runtime", {
    "moduleName": "@babel/runtime",
    }]
    ],
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": "usage",
    "debug": true
    }
    ]
    ],
    }

    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var _Promise = require("@babel/runtime/core-js/promise");
    var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new _Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例六

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "plugins": [
    ["@babel/plugin-transform-runtime", {
    "moduleName": "@babel/runtime",
    }]
    ],
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": "entry",
    "debug": true
    }
    ]
    ],
    }

    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import "@babel/polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    "use strict";
    var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
    var _promise = _interopRequireDefault(require("@babel/runtime/core-js/promise"));
    var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
    require("core-js/modules/es6.array.sort");
    require("core-js/modules/es6.date.to-json");
    require("core-js/modules/es6.typed.array-buffer");
    require("core-js/modules/es6.typed.int8-array");
    require("core-js/modules/es6.symbol");
    require("core-js/modules/es6.number.parse-int");
    var Person = function Person() {
    (0, _classCallCheck2.default)(this, Person);
    };
    var a = new _promise.default(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    当 @babel/preset-env 中的 useBuiltIns 为 false 的时候和 usage 是一样的(但是必须是 test.js 中没有有 import polyfill)。

  • Babel 6

    例一

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "presets": [
    [
    "babel-preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": true,
    "debug": true
    }
    ]
    ],
    }

    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    'use strict';
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例二

    .babelrc 同例一。
    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import "babel-polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    'use strict';
    require('core-js/modules/es6.typed.array-buffer');
    require('core-js/modules/es6.typed.int8-array');
    require('core-js/modules/es6.typed.uint8-array');
    require('core-js/modules/es6.typed.uint8-clamped-array');
    require('core-js/modules/es6.typed.int16-array');
    require('core-js/modules/es6.typed.uint16-array');
    require('core-js/modules/es6.typed.int32-array');
    require('core-js/modules/es6.typed.uint32-array');
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例三

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "presets": [
    [
    "babel-preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": false,
    "debug": true
    }
    ]
    ],
    }

    test.js 同例二。
    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    'use strict';
    require('babel-polyfill');
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    var Person = function Person() {
    _classCallCheck(this, Person);
    };
    var a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例四

    .babelrc:

    1
    2
    3
    4
    5
    {
    "plugins": [
    "transform-runtime"
    ],
    }

    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import _Promise from 'babel-runtime/core-js/promise';
    class Person {}
    let a = new _Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例五

    .babelrc 和例四一样。
    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import "babel-polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import _Promise from 'babel-runtime/core-js/promise';
    import "babel-polyfill";
    class Person {}
    let a = new _Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例六

    .babelrc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "plugins": [
    "transform-runtime"
    ],
    "presets": [
    [
    "babel-preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": true,
    "debug": true
    }
    ]
    ],
    }

    test.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import "babel-polyfill";
    class Person {}
    let a = new Promise(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    'use strict';
    var _promise = require('babel-runtime/core-js/promise');
    var _promise2 = _interopRequireDefault(_promise);
    var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
    var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
    require('core-js/modules/es6.typed.array-buffer');
    require('core-js/modules/es6.typed.int8-array');
    require('core-js/modules/es6.typed.uint8-array');
    require('core-js/modules/es6.typed.uint8-clamped-array');
    require('core-js/modules/es6.typed.int16-array');
    ...还有其它兼容插件
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    var Person = function Person() {
    (0, _classCallCheck3.default)(this, Person);
    };
    var a = new _promise2.default(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    例七

    .babelrc 和例六一样。
    test.js和例六比去掉

    1
    import "babel-polyfill"

    test-compiled.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    'use strict';
    var _promise = require('babel-runtime/core-js/promise');
    var _promise2 = _interopRequireDefault(_promise);
    var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
    var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    var Person = function Person() {
    (0, _classCallCheck3.default)(this, Person);
    };
    var a = new _promise2.default(function (resolve, reject) {
    console.log('Promise');
    resolve();
    });
    a.then(function () {
    console.log('resolved.');
    });
    console.log('Hi');

    设置 .babelrc 中 useBuiltIns 为 false 和上面是一样的结果。
    当设置 .babelrc 中 useBuiltIns 为 false 并且test.js中添加

    1
    import "babel-polyfill";

    则输出会多一句

    1
    require('babel-polyfill');

    当设置 .babelrc 中 useBuiltIns 为 true 并且 test.js 中添加

    1
    import "babel-polyfill"

    则输出会多

    1
    2
    3
    4
    require('core-js/modules/es6.typed.int16-array');
    require('core-js/modules/es6.typed.uint16-array');
    require('core-js/modules/es6.typed.array-buffer');
    ...还有其它

    总结

    这里有一个非常重要的问题就是关于transform-runtime官方文档上写的关于有些方法必须使用 babel-polyfill 中的方法。

    那么这样导致的结果就是如果是在 Babel 6 中的时候当在源文件中写上“foobar”.includes(“foo”)就必须写上import “babel-polyfill”;

    但是 Babel 7 就不需要在源文件中写_import "@babel/polyfill";_会智能判断是否需要去引用这个includes的 polyfill。

    test.js中有import “babel-polyfill”;的时候,Babel 6 版本的“useBuiltIns”: true即是 Babel 7 的“useBuiltIns”: “entry”

    在 Babel 7 中即使是源文件中不写import “@babel/polyfill”;当设置“useBuiltIns”: “usage”会根据情况输出“@babel/polyfill”对应兼容插件的输出。

    安装babel-polyfill

    当是 Babel 7 的时候使用 .babelrc 配置建议如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "plugins": [
    "@babel/plugin-transform-runtime"
    ],
    "presets": [
    [
    "@babel/preset-env",
    {
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": "usage",
    }
    ]
    ],
    }

    Babel 6

    如果有使用 babel-polyfill。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "plugins": [
    "transform-runtime"
    ],
    "presets": [
    ["env",{
    "targets": {
    "browsers": ["last 2 versions", "safari 7"]
    },
    "useBuiltIns": true,
    }
    ]
    ],
    }

    Babel 6 webpack 中的配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const path = require('path');
    module.exports = {
    entry: {
    index: ["babel-polyfill", "./test.js"]
    },
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    },
    module: {
    rules: [
    {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
    loader: 'babel-loader'
    }
    },
    ]
    }
    };

    Babel 7 webpack 配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const path = require('path');
    module.exports = {
    entry: ["./test.js"],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    },
    module: {
    rules: [
    {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
    loader: 'babel-loader'
    }
    },
    ]
    }
    };