Since @babel/polyfill is no longer recommended after Babel 7.4, both @babel/preset-env and plugin-transform-runtime can set corejs to handle polyfill.

The main reasons for the deprecation of @babel/polyfill are.

  • This package simply introduces stable core-js and regenerator-runtime/runtime, where the latter can be replaced using the plugin @babel/plugin-transform-regenerator.
  • This package does not smoothly transition from core-js@2 to core-js@3.

Introduction to Babel

In short, Babel is a compiler that converts code written in ECMAScript 2015+ syntax to backward-compatible JavaScript syntax so that it can run in various environments, including new and old versions of browsers. The Babel code conversion is implemented as plugin, which is a small JavaScript program.

A preset can be thought of as a set of Babel plugins or shareable modules configured with options.

  • plugins run before presets.
  • plugins are executed in front-to-back order.
  • presets are executed in reverse order according to the order in which they are listed.

The two main functions that babel implements

  1. convert the new syntax. Implementing the new version of js syntax in the old version so that it runs in the corresponding environment, e.g. arrow functions.

  2. converting the new API. patching the old runtime (also called polyfill) so that it uses functionality defined in the new version of js but provided in the old runtime, including three categories.

    • Newly defined built-in objects, such as Promise
    • New static methods added to existing built-in objects, such as Array.from
    • New instance methods added to the original built-in object, such as Array.prototype.includes

preset-env

preset-env converts both the new syntax and the new API by configuration. preset-env’s polyfill will pollute the global environment.

target

This field can be filled with the query string of browserslist. It is officially recommended to use the .browserslistrc file to specify the target of the compilation, and this configuration file can also share configuration with tools like autoprefixer and stylelint. So it is not recommended to use targets directly in the preset-env configuration of .babelrc.

If you need to configure targets separately here, specify ignoreBrowserslistConfig as true in preset-env and ignore the .browserslistrc configuration item.

useBuiltIns

Whether to use its polyfill function (core-js for the global environment). There are three values.

  • false: the default value. Does not use preset-env for polyfills without active import, and only uses its default syntax conversion functionality. If you use the default value false, you should avoid introducing polyfill in the entry file, making the package too large.

  • entry: You need to manually introduce polyfill in the entry, and introduce all polyfill modules that are not supported by the browser, whether they are used in the project or not, depending on the configuration of the browser’s target environment ( targets).

    1
    
    import "core-js/proposals/string-replace-all"
    
  • usage: Instead of introducing polyfill manually in the entry file, Babel will automatically inject polyfill based on the code usage, which will reduce the package size relatively much when packaging.

corejs

Configure core-js with the default value of 2.0. This option is only valid when used with useBuiltIns: usage or useBuiltIns: entry.

core-js: JavaScript’s modular standard library, containing Promise, Symbol, Iterator and many other features that allow you to load only the necessary functionality.

  • version: [string] version number.
  • proposals: [boolean] Whether to implement the features in the proposals.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// .babelrc
{
  "presets": [    
    [      
      "@babel/preset-env",      
      {        
        "targets": {          
          "chrome": "80" // 推荐使用 .browserslistrc        
        },        
        "useBuiltIns": "usage",        
        "corejs": {          
          "version": 3, // 2 和 3 版本都需要手动安装库:yarn add core-js@3 
          "proposals": false        
        }      
      }    
    ]  
  ]
}

plugin-transform-runtime

plugin-transform-runtime does three main things.

  • Automatically introduces @babel/runtime/regenerator when developers use asynchronous or generators, so developers don’t have to do additional introductions in the entry file.
  • Dynamically introducing polyfill to provide a sandboxed environment and avoid contamination of the global environment; these can contaminate the global if imported directly into core-js or @babel/polyfill and the built-in components it provides such as Promise, Set and Map. While this does not affect applications or command-line tools, it can be a problem if the code is a library to be published for use by others, or if there is no precise control over the environment in which the code will run.
  • All helpers helper modules will reference the module @babel/runtime to avoid duplication in the compiled output and to reduce packaging size.

corejs

Configuration values: false, 2, or { version: number, proposals: boolean } , default value false.

corejs Installation suggestions
false npm install –save @babel/runtime
2 npm install –save @babel/runtime-corejs2
3 npm install –save @babel/runtime-corejs3

helpers

Configuration value boolean type, default value true. Whether to replace inline Babel helpers (classCallCheck, extends, etc.) with calls to moduleName.

regenerator

Configuration value of type boolean, default true. Whether to convert the generator function to use a regenerator runtime that does not pollute the global scope.

useESModules

Configuration value boolean type, default false. When enabled, conversions will use helpers instead of @babel/plugin-transform-modules-commonjs. This allows for smaller builds in module systems like webpack, as it does not need to preserve commonjs semantics.

Usage scenarios and example analysis

Both @babel/preset-env and plugin-transform-runtime can be set to use corejs to handle polyfill, each with its own usage scenario, and different configurations can be used for project development and library development.

Don’t configure core-js for both, to avoid complex and undesirable consequences.

Project development

useBuiltIns uses usage. plugin-transform-runtime only uses its feature of removing inline reused helper functions to reduce package size.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": {
          "version": 3,
          "proposals": false
        }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": false
      }
    ]
  ]
}

Library development

Class library development tries not to use polyfill that pollutes the global environment, so @babel/preset-env only functions as a syntax transform, polyfill is handled by plugin-transform-runtime, core-js@3 is recommended, and features that are not in the specification are not used.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": false
        },
        "useESModules": true
      }
    ]
  ]
}

Package analysis

The test code is as follows

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// syntax
class Person {}
typeof Person
const array = [1, 2, 3]
const fun = () => {}

// api
const a = Array.isArray([3, 5, 8])
const b = array.map(itm => itm * 2)

const p1 = Promise.resolve(true)
const p2 = Promise.reject(false)
Promise.allSettled([p1, p2]).then(() => {
  console.log('then')
}).catch(() => {
  console.log('catch')
}).finally(() => {
  console.log('finally')
})

Configuring core-js via preset-env

 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
43
44
45
46
47
48
49
50
51
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/es.array.is-array.js");

require("core-js/modules/es.array.map.js");

require("core-js/modules/es.object.to-string.js");

require("core-js/modules/es.promise.js");

require("core-js/modules/es.promise.finally.js");

require("core-js/modules/es.array.iterator.js");

require("core-js/modules/esnext.promise.all-settled.js");

require("core-js/modules/es.string.iterator.js");

require("core-js/modules/web.dom-collections.iterator.js");

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

// syntax
var Person = /*#__PURE__*/(0, _createClass2["default"])(function Person() {
  (0, _classCallCheck2["default"])(this, Person);
});
(0, _typeof2["default"])(Person);
var array = [1, 2, 3];

var fun = function fun() {}; // api


var a = Array.isArray([3, 5, 8]);
var b = array.map(function (itm) {
  return itm * 2;
});
var p1 = Promise.resolve(true);
var p2 = Promise.reject(false);
Promise.allSettled([p1, p2]).then(function () {
  console.log('then');
})["catch"](function () {
  console.log('catch');
})["finally"](function () {
  console.log('finally');
});

configure core-js via plugin-transform-runtime

 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 _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _isArray = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/is-array"));

var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/typeof"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));

// syntax
var Person = /*#__PURE__*/(0, _createClass2["default"])(function Person() {
  (0, _classCallCheck2["default"])(this, Person);
});
(0, _typeof2["default"])(Person);
var array = [1, 2, 3];

var fun = function fun() {}; // api


var a = (0, _isArray["default"])([3, 5, 8]);
var b = (0, _map["default"])(array).call(array, function (itm) {
  return itm * 2;
});

var p1 = _promise["default"].resolve(true);

var p2 = _promise["default"].reject(false);

_promise["default"].allSettled([p1, p2]).then(function () {
  console.log('then');
})["catch"](function () {
  console.log('catch');
})["finally"](function () {
  console.log('finally');
});

Analysis of the packaged code shows that preset-env introduces polyfill that is not necessary, and plugin-transform-runtime introduces only polyfill that is needed for the current page.