Babel core concepts

Babel is already available in 7.x versions, and core-JS, which is closely related to Babel, is also available in 3.x versions, with babel7.x and core-js3.x as counterparts. There are a lot of new changes in babel7.x and core-s3. X that are worth learning, and many current businesses still use babel6.x. Consider upgrading babel6.x in existing projects, or new projects, definitely directly on the new configuration of babel7.x.

Starting with this article, I’ll try to summarize in detail what the latest version of Babel (7.x) does and uses. After looking at the latest documentation of Babel and Core-js over the last few days, I think there are some key points to mastering Babel7.x proficiently:

  • What is the Babel
  • What do Babel’s plugins do
  • What does Babel presets do
  • babel/polyfillFor what? What’s changed now
  • What is thetransform-runtime
  • What is core-js, how does it integrate with Babel, and how does core-js@3 change from core-js@2
  • What is the Babel/register
  • How do I choose how to configure Babel? What are the application scenarios of each configuration mode
  • Babel configuration options the meanings and functions of each option
  • What are some common ways to use Babel
  • How do I upgrade Babel7.x, and how is babel7.x supported by other tools that work with Babel, such as WebPack?
  • What does Babel’s API do

There are a lot of points to cover. If you can handle all of the above, you’ll probably have no problems with Babel bugs or engineering problems.

This paper records and summarizes some basic concepts such as Babel.

1. The Babel is what

Simply put, Babel is a translator of JS code. Its main usage scenarios are:

  • Converting ES6 code to ES5 code so that you can use the latest syntax to improve efficiency and quality during development, even if the final environment (common: browser) does not support ES6.

  • Babel uses core-JS pairs to automatically polyfill JS code so that the code that ends up running in the browser can use those apis. Babel always provides support for the latest ES proposal;

  • Because Babel can process the code, some big factory teams use Babel to do source code processing (Codemods), because there are many people in big factory, everyone has different development habits, in order to standardize the code style, improve the code quality, with the help of Babel, everyone’s source code to do certain standardized conversion processing, Can improve the overall development quality of the team;

  • The Vue framework recommends writing components in a single-file. Vue format, which also uses Babel to convert.vue files into standard ES code.

  • The React framework generally uses JSX syntax to write templates, but the Vue framework can also use JSX. With Babel, JSX can be converted to ES code correctly.

  • Js itself is a weakly typed language, but in large factories, many people are particularly easy to cause serious bugs because of some small errors in the language itself, so if before the code online, with the help of strong typed language static analysis ability, to do some checks on JS code, you can avoid many problems. Flow and typescript are currently two mainstream strongly typed JS programming methods. Babel can convert Flow and typescript code into standard ES code.

  • Babel, because it transforms code, optionally generates sourcemap for code to troubleshoot special problems;

  • Babel does its best to conform to the ES specification. When learning Babel’s presets and plugins, you may see the spec option. If this option is enabled, the presets and plugins will conform to the specification more properly. To convert ES code, except that the option is usually false, meaning not to enable the Babel conversion is faster;

  • Es6 modules are not supported very well at present. Babel provides another solution, which can automatically convert modules written in ES6 when required. Although there is no way to evaluate this solution. But you can also feel that Babel is working hard to get developers to use the latest ES syntax;

Out of the box, Babel does nothing. If Babel is not configured, a piece of ES6 code is processed by Babel and remains the same. All of Babel’s functions are implemented through plugins. Each plugin has a specific code handling function that can only be converted if it is configured to run inside Babel. Babel currently has a number of plugins, both official and third-party.

Because there are too many plug-ins, it is very difficult to configure each time. Each project requires a similar set of plugins, so Babel introduced presets. Each preset contains several plugins. Means that all of the plug-ins included in this preset are likely to participate in the conversion of the code. Common preset are: preset-env, preset-state-0,preset-stage-2, and so on.

Plugins and presets need to be configured to take effect. In addition to these two options, Babel also has many other options that can be configured. Only after you understand the function of each option, can you flexibly use them in your project. So Babel options is also one of the goals of learning Babel.

2. What are the usage scenarios of Babel

Babel options can be configured in a variety of ways, including package.json, babel.config.js, json-formatted. Babelrc file, and js format. Since Babel has command line tools, options can also be configured directly on the Babel command line. Because Babel provides an API that allows developers to manually call Babel to convert code, options can also be configured when the API is called.

There are a number of ways to call Babel, but the bottom line is to call Babel directly from the API; All the other ways that I’m going to talk about are tied to this form. Babel provides the command line tool babel-CLI, which allows you to convert files or folders directly from the command line, which is also a convenient way to test some of Babel’s features.

Babel can be used in conjunction with build tools such as Webpack and gulp. When combined with them, Babel is automatically called by the build tools. This is the most common entry point for Babel in front-end development. Babel can also be used with lint tools such as ESLint. In the Node environment, Babel provides babel-Register, which automatically converts ES6 files to ES5 code when loaded by Node.

ES6 syntactically converts to ES5. Babel can do this, but some of the new apis are not syntactically unsupported, but the runtime environment of ES5 has no corresponding implementation. So an important part of Babel’s job is to polyfill code. Before Babel7, Babel provided a library called Babel/Polyfill to do this. After Babel7, this library was abandoned because Polyfill was used in a new way. This is one of the most important things babel7.x learns.

Because Babel uses a number of its own utility functions called helpers during the transformation process. When not optimized, each file contains the code for these helpers individually, which can lead to a lot of duplicated code, so Babel developed the Transform-Runtime to automate extraction and other optimizations for these helpers.

Babel polyfills the code with two other libraries: core-js and Regenerator-Runtime. Core-js is currently upgraded to 3.x, which is quite different from 2.x. There is no change to the regenerator-Runtime. The [email protected] version is also worth studying. It is likely that you will use the library directly in the future, so you need to understand how it organizes the various module implementations of ES.

These are some of the concepts and points that are closely related to using Babel. Babel is always working with the latest ES specification, so you should be familiar with ESMAScript concepts.

3. ESMAScript stage

A new grammar goes through five stages from proposal to formal standard. Changes at each stage need to be approved by the TC39 Committee.

  • Stage 0 – Strawman
  • Stage 1-Proposal
  • Stage 2 – Draft
  • Stage 3
  • Stage 4-Finished

All current tc39 proposals can be viewed on Github: github.com/tc39/ecma26…

This means that only stage 4 proposals will be included in the current release of the new standard each July. The term ES6 is generally used to refer to all ES releases after THE release of ES2015, including ES6, 8, 9 and 10. When people say ES8, 9, 10, they really mean ES2017, ES2018, ES2019. With that in mind, it would be a good way for a front-end developer to keep an eye on what TC39 is proposing each year, what new ESMA-262 is being released each year, and what features are being added to the standard.

4. What does the @babel symbol in babel7.x mean

Starting with Babel 7.0, the Babel series of packages begin with @babel, which has nothing to do with Babel and is a form of NPM package. In short, packages that start with the @ sign, such as @babel/preset-env, represent a class of scope-bound NPM packages. Scope usually means a company, an institution or even an individual. Its function is to organize and manage related packages of the same subject in a scope, which is scope. The biggest advantage of this kind of package with scope is that only the scope’s main company, institution and individual can add new packages to the scope, and no one else can. That is to say, the NPM package starting with @ must be officially launched or officially approved by the launch of the package, more authoritative nature.

Babel Pugins

Out of the box, Babel does nothing. If you want it to transform your code, you need to configure plugins for it to work. This article describes the use of plugins for Babel and some changes to the plugin in Babel7.

1.babel-cli

In order to learn how to use plugin, we need to learn a way to use Babel first. Babel-cli is a good choice because it is simple enough. Now, create a new folder and run it under it:

npm install @babel/core --save-dev
npm install @babel/cli --save-dev
Copy the code

Babel’s core library and Babel’s command-line management tool, babel-CLI, are installed into the dependencies of the project.

Next, under this folder, create a.babelrc.js file, which is a JS file that is one of the ways to configure Babel; When we learn about plugin, we will write plugin to.babelrc.js so that when Babel is running, plugin configured in this file will be enabled.

Finally, to have a place to write the test code and a place to see Babel after the code conversion, add two more subfolders, SRC and dist, to this folder. The final project folder structure is shown as follows:

babel-plugin/
    dist/
    node_modules/
    src
    .babelrc.js
    package.json
Copy the code

To demonstrate the use of babel-cli, add a js file test01.js to SRC and write the following ES code:

let foo = () = >{};Copy the code

Then switch to the babel-plugin/ folder on the terminal and run the following command:

npx babel src --out-dir dist
Copy the code

This command will input all js files from the SRC folder into Babel, and after the conversion, store the output in the dist folder. Since Babel is not currently configured, you should only see dist/test01.js with some formatting changes but the same code content when you run the command above.

The rest will be tested using the little environment prepared above.

2. The use of plugins

When configuring Babel using the.babelrc.js file, the configuration structure of the file is usually:

const presets = [  ];
const plugins = [ ];

module.exports = { presets, plugins };
Copy the code

The presets array is used to configure Babel’s presets, and the plugins array is used to configure Babel’s plugins. Both presets and plugins can be configured multiple times.

3. The name of the plugin

Before you start using it, say the name of the plugin. Starting with babel7, all Babel packages, not only plugin packages, but preset packages, are all changed to scope packages in the form of @babel. Such as:

@babel/plugin-transform-arrow-functions for arrow function transcoding @babel/plugin-transform-block-scoping block level scope transcoding @babel/ plugin-transform-for-for-of etcCopy the code

Babeljs. IO /docs/en/plu…

If you want to use babel7, make sure that a plugin is not Babel’s own package. If it is, use @babel to install it, otherwise it will probably be installed in the related package of Babel6. If it’s not Babel’s own package, it certainly can’t be installed with the @babel scope, just the name it identifies. For example, transform-arrow-functions plugin, if it is babel6, its NPM package name is: Babel-plugin-transform-es2015 arrow-functions @babel/plugin-transform-arrow-functions

The plugin name of babel7 differs from that of babel6 by replacing the prefix Babel – with @babel/. For example, babel-plugin-transform-es2015-arrow-functions removes es2015 characters from babel7. This is one of the other changes made to plugin in Babel7:

Babel7 removes the es versions of plugin package names, such as ES3 and ES2015. Another big change to the plugin name is:

If a plugin is converting a feature that is not officially released by ECMA-262 each year (ES2015, ES2016, etc), the name of the plugin will be renamed the -proposal modified name. The obvious purpose of this is to make plugin usage scenarios clearer; Furthermore, the plugin package name will be renamed once the feature being converted has entered Stage 4 of the TC39 workflow (Stage 4 means the feature will be released in ecMA-262 next year).

In summary, when using the Babel7 plugin in the future, be a little more careful with the package name and check the readme description on its NPM or Github home page to prevent low-level errors.

4. The classification of the plugin

Babel’s plugins fall into three categories:

  • syntaxThe grammar class
  • transformTransform class
  • proposalAlso a transformation class, which refers to the plugin that transforms ES Proposal.

If you look at Babel’s Github code structure, you can clearly see the source folder names of the above three plug-ins: github.com/babel/babel…

Syntax plugin is used to convert ES new syntax. In fact, syntax plugin is also necessary when using ES new syntax. However, when using a transform or proposal plug-in, if some syntax conversion is needed, the corresponding syntax plug-in will be automatically enabled. The Syntax class plugin does not need to be configured separately. For example, the transform plugin, which converts typescript, relies on the @babel/plugin-syntax-typescript syntax plugin, as shown in package.json:

{
  "name": "@babel/plugin-transform-typescript"."version": "7.5.5"."description": "Transform TypeScript into ES.next"."dependencies": {
    "@babel/helper-create-class-features-plugin": "^ 7.5.5." "."@babel/helper-plugin-utils": "^ 7.0.0." "."@babel/plugin-syntax-typescript": "^ 7.2.0"
  },
  "peerDependencies": {
    "@babel/core": "^ 7.0.0-0"
  },
  "devDependencies": {
    "@babel/core": "^ 7.5.5." "."@babel/helper-plugin-test-runner": "^ 7.0.0." "}}Copy the code

5. Example

Open the small project.babelrc.js file and put the following in it:

const presets = [];
const plugins = [];

module.exports = {presets, plugins}
Copy the code

We’re going to add something to plugins, so let’s try Babel.

Plugins are an array, each element of which represents a separate plugin:

["pluginA"["pluginA", {}]]
Copy the code

There are two types of elements:

  • A pure string that identifies a plugin
  • The first element of this array is a string that identifies a plugin, and the second element is an object literal that passes the options configuration to the plugin

Plugins, which are pure strings, are a simplified use of the array form. Plugins can be configured with options, so plugins are strings that use the default values of options.

Examples are as follows:

const plugins = [
    '@babel/plugin-transform-arrow-functions'["@babel/plugin-transform-async-to-generator",
      {
         "module": "bluebird"."method": "coroutine"}]].Copy the code

How to identify a plugin with a string?

If plugin is an NPM package, the name of the NPM package can be used directly. If Plugin is a local file, the file can be referenced in relative or absolute paths to identify the plugin

What is the abbreviation of the Plugin identifier? For historical reasons, Babel provided a rule for abbreviating the plugin’s identity configuration. It is possible to omit babel-plugin- whenever a plugin begins with babel-plugin-, as both approaches are equivalent:

const plugins = [
    [
        "myPlugin"."babel-plugin-myPlugin" // equivalent]].Copy the code

If a plugin is a scope package, the same abbreviations rule holds:

const plugins = [
    "@org/babel-plugin-name"."@org/name" // equivalent
]
Copy the code

Note: babel-plugin-myplugin is not necessarily Babel’s own package; @org does not refer to @babel, and other organizations can publish their own Babel packages as Scope packages.

Babel7 has a new abbreviation rule because packages are scope packages:

const plugins = [
    '@babel/transform-arrow-functions'.// equivalent to @babel/plugin-transform-arrow-functions
    [
      "@babel/transform-async-to-generator".// equivalent to @babel/plugin-transform-async-to-generator
      {
         "module": "bluebird"."method": "coroutine"}]].Copy the code

However, it seems that the authors of Babel do not feel the need to abbreviate the plugin. In the future, the abbreviation rule will be removed, so in practice, it is better to refer to a plugin and use the full NPM package name, which has few more words.

To demonstrate the power of the plugin, we can use several typical ES6 features to apply the plugin:

@babel/plugin-transform-classes This plugin can transform ES6class
@babel/plugin-transform-arrow-functionsthispluginCan convertES6The arrow function at signbabel/plugin-transform-computed-propertiesthispluginCan convertES6Property name expression ofCopy the code

After the installation is successful, configure them into the.babelrc.js file:

const presets = [];
const plugins = [
    '@babel/plugin-transform-arrow-functions'['@babel/plugin-transform-classes'].'@babel/plugin-transform-computed-properties'
];

module.exports = {presets, plugins}
Copy the code

Next, in the SRC directory, continue to edit test01.js and replace it with:

// Arrow function
let foo = () = >{};// ES6 class
class List {
    constructor(pi = 1, ps = 10) {
        this.pi = 1;
        this.ps = 10;
    }

    loadData(){}static genId(){
        return ++this.id; }}let name = 'lyzg';

let obj = {
    baseName: name,
    [name + '_id'] :'baseName'
};
Copy the code

This is a snippet of ES6 code that includes arrow functions, class and attribute name expressions. Then run NPX Babel SRC –out-dir dist, open dist/01.js, and you should see the following transformed code:

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true.configurable: true.writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if(! (instanceinstanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); }}function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

// Arrow function
let foo = function () {}; // ES6 class


let List =
/*#__PURE__*/
function () {
  function List(pi = 1, ps = 10) {
    _classCallCheck(this, List);

    this.pi = 1;
    this.ps = 10;
  }

  _createClass(List, [{
    key: "loadData".value: function loadData() {}}], [{key: "genId".value: function genId() {
      return ++this.id; }}]);returnList; } ();let name = 'lyzg';

let obj = _defineProperty({
  baseName: name
}, name + '_id'.'baseName');
Copy the code

That’s the core usage of the Babel plug-in.

If you look carefully, you can see that dist/test01.js also contains the let keyword, which indicates that the three plug-ins I used do not have the function of let declaration transcoding. Therefore, when we choose to configure our own plug-ins for code conversion, we should be very clear about the plug-ins we need. In terms of ES6class, there is more than one transcoding plugin for @babel/plugin-transform-classes. Not all features of ES6class are released in ES2015, and some new features are still in the proposal stage. So if you want to use the full ES6class, you need to know more about the plug-ins. Since assembling plugins yourself can be a hassle, Babel has introduced Presets to simplify the use of plugins.

6.transform plugins

Babel’s Transform Plugins fall into the following categories:

  • ES3 converts some features of ES3
  • ES5 converts some features of ES5
  • ES2015 transforms ES6 features, and most plugins fall into this category
  • ES2016 converts ES7 features
  • ES2017 converts ES8 features
  • ES2018 converts ES9 features
  • Modules Automatically converts the module organization of code
  • Experimental proposal feature conversion
  • Minification, a plug-in under this category, is not deployed in @babel, but is managed as a separate library: Babel/Minify. The library is an experimental project and has not been released, so Babel is not recommended for production use
  • React is used for React code conversion
  • Other Other

Click here for details of the above categories

7.syntax plugins

Babel syntax plugins are not used to convert code, but to parse new syntax-specific features of ES6, and if you use the Syntax Plugin directly, the code does not convert at all. To transform the new syntax, you must use the corresponding Transform plugins. The syntax plugin is relied upon by the Transform Plugin for syntax parsing.

8. Sequence of plugin enablement

As you know, Babel is based on plugins, and plugins can be configured in multiple ways. Babel also provides Preset, which is basically a set of plugins. If there are so many plugins, there must be a sequence in which the source code is parsed, and the results of the previous plugin will serve as input to the next plugin. So Babel specifies the order in which plugins are enabled:

  • Plugins are configured directly within plugins, prior to plugins in presets.
  • Plugins in the plugins array are enabled in order of the array index.
  • Presets in the presets array are enabled in reverse order based on the array index, that is, apply the last presets first and then preset.

Such as:

{
  "plugins": ["transform-decorators-legacy"."transform-class-properties"]}Copy the code

Transform-decorators-legacy is enabled first, followed by transform-class-properties.

{
  "presets": ["es2015"."react"."stage-2"]}Copy the code

Preset enablement order: stage-2 React ES2015

9. The plugin’s options

The official Babel documentation explicitly states the configuration of plugin and preset, and plugin and preset are the same: The options of each plugin are different. This article notes several options that appear frequently in official Babel documentation:

  • loose

Enable loose code conversion. If a plug-in supports this option, the converted code is simpler and less code, but does not strictly follow the ES specification, which is usually false by default

  • spec

Enable ES spec code conversion, the default is false, converted code, a lot of helper functions, more code, but better code quality

  • legacy

Enable the old implementation to transform the code. See the examples below

  • useBuiltIns

If true, instead of introducing polyfills, the conversion process uses implementations already supported by the runtime environment as much as possible

For example: @babel/plugin-proposal-object-rest-spread. In babel7, the default conversion behavior is the same as spec: true, so it no longer provides the option of spec.

letbar = {... obj};Copy the code

To:

function ownKeys(object, enumerableOnly) { ...; }

function _objectSpread(target) { ...; }

function _defineProperty() { ...; }

let bar = _objectSpread({}, obj);
Copy the code

This plugin supports both loose and useBuiltIns options. If loose is enabled, the code is converted to:

function _extends() { ...; }

let bar = _extends({}, obj);
Copy the code

If loose and useBuiltIns are enabled at the same time, the code is converted to:

let bar = Object.assign({}, obj);
Copy the code

Look! Loose and useBuiltIns make the transformed code simpler and simpler, but they also move further and further away from the requirements expressed in the ES specification.

  • legacy

Because ES6 decorators syntax has been written in a new way, babel7 uses the @babel/plugin-proposal-decorators plugin to convert ES6 decorators syntax by default, enabling the new transcoding. If you are still using the old ES6 decorators syntax, you should enable Legacy Option when using this plug-in so that the plug-in can still transcode the old syntax.

Babel preset

Babel’s Presets are designed to simplify the use of plugins. This article introduces the Babel Presets.

Official recommended Preset

There are four official recommended preset versions:

  • @babel/preset-envAll projects will use it
  • @babel/preset-flowThe needs of the flow
  • @babel/preset-reactThe React framework requires
  • @babel/preset-typescriptThe typescript need

Other preset, such as state-2, state-3, ES2015, are not recommended since Babel7.

1. Customize a preset

To create a preset of its own, it is easy to create a module of the following form:

module.exports = function() {
  return {
    plugins: [
      "pluginA",
      "pluginB",
      "pluginC",
    ]
  };
}
Copy the code

This simple file above can be used as a Babel preset. The plugins defined in Babel take effect when preset is referred to in the configuration of Babel. Aside from plugins, preset can also contain other presets, as long as a form like the following is defined:

Take a closer look, preset is the return value of the export function and is similar to the configuration in.babelrc.js. As such, preset and plugin are configured in this place just as we configure preset and plugin in.babelrc.js.

So it’s easy to define a preset that is only used as a plugin set.

2. Preset configuration and use

Preset is configured in exactly the same way as plugin. Check the official description for the Plugin/Preset configuration item

Continue with the small project used in the previous article and create a new file in the project root directory, my-preset. Js:

module.exports = () => ({
  plugins: [
    ['@babel/plugin-transform-arrow-functions'],
    ['@babel/plugin-transform-classes', {spec: false}],
    ['@babel/plugin-transform-computed-properties'],
    ['@babel/plugin-proposal-object-rest-spread', {loose: true, useBuiltIns: true}]
  ]
});
Copy the code

Move all plugins configured in.babelrc.js in the last article to my-preset. I want to preset this file as a preset to.babelrc.js and try out the result of Babel conversion. .babelrc.js was eventually changed to:

const presets = [
    './my-preset.js'
];
const plugins = [
];

module.exports = {presets, plugins}
Copy the code

Then run NPX Babel SRC — out-of-dir dist to view the transcoding result of the custom PRESET configuration.

3. The name of the preset

When I introduced the plugin’s name last time, I tried to explain how it works from my point of view. But there’s actually a section of the Official Babel documentation that details the rules of plugin/preset, and there’s a special Name called Name Normalization that includes various Name types. Such as relative path, absolute path, NPM package, scope NPM package and the name of the shorthand rule. Please refer to

4. Preset is in order

As mentioned in the previous article, presets can be configured multiple times, but preset is enabled in the reverse order of its indexing in the presets configuration array.

5. preset options

Different preset supports different options, and options are configured in the same way as plugin, for example:

{
  "presets": [
    ["@babel/preset-env", {
      "loose": true,
      "modules": false
    }]
  ]
}
Copy the code

6. @babel/preset-env

This is the most important Preset in Babel right now, and preset is much more complex than the custom preset above, so it’s worth learning about preset in detail. The preset, can according to our configuration of browserslist, when transcoding automatically according to the goal of our counter rotating code code after running environment of the minimum required version, use more “intelligent” transcoding, if we set the minimum version of the environment, has been to native and ES characteristics of transcoding, will direct the ES standard writing; If the feature to transcode is not yet supported in the lowest release environment, the corresponding Polyfill is automatically injected.

Browserslist should be familiar, and there are a lot of front-end build tools that are related to browserslist. In addition to Browserslist, @babel/preset-env relies on two other libraries for its implementation: compat-table, and electron-to-Chromium. The latter two help presets -env, know ES6 features, which version is native supported in different platforms and environments.

@babel/preset-env does not support all stage-x plugins. Look at the package.json file for preset-env to see which plugins it needs:

{
  "name": "@babel/preset-env"."version": "7.5.5"."dependencies": {
    "@babel/helper-module-imports": "^ 7.0.0." "."@babel/helper-plugin-utils": "^ 7.0.0." "."@babel/plugin-proposal-async-generator-functions": "^ 7.2.0"."@babel/plugin-proposal-dynamic-import": "^ 7.5.0"."@babel/plugin-proposal-json-strings": "^ 7.2.0"."@babel/plugin-proposal-object-rest-spread": "^ 7.5.5." "."@babel/plugin-proposal-optional-catch-binding": "^ 7.2.0"."@babel/plugin-proposal-unicode-property-regex": "^ 7.4.4." "."@babel/plugin-syntax-async-generators": "^ 7.2.0"."@babel/plugin-syntax-dynamic-import": "^ 7.2.0"."@babel/plugin-syntax-json-strings": "^ 7.2.0"."@babel/plugin-syntax-object-rest-spread": "^ 7.2.0"."@babel/plugin-syntax-optional-catch-binding": "^ 7.2.0"."@babel/plugin-transform-arrow-functions": "^ 7.2.0"."@babel/plugin-transform-async-to-generator": "^ 7.5.0"."@babel/plugin-transform-block-scoped-functions": "^ 7.2.0"."@babel/plugin-transform-block-scoping": "^ 7.5.5." "."@babel/plugin-transform-classes": "^ 7.5.5." "."@babel/plugin-transform-computed-properties": "^ 7.2.0"."@babel/plugin-transform-destructuring": "^ 7.5.0"."@babel/plugin-transform-dotall-regex": "^ 7.4.4." "."@babel/plugin-transform-duplicate-keys": "^ 7.5.0"."@babel/plugin-transform-exponentiation-operator": "^ 7.2.0"."@babel/plugin-transform-for-of": "^ 7.4.4." "."@babel/plugin-transform-function-name": "^ 7.4.4." "."@babel/plugin-transform-literals": "^ 7.2.0"."@babel/plugin-transform-member-expression-literals": "^ 7.2.0"."@babel/plugin-transform-modules-amd": "^ 7.5.0"."@babel/plugin-transform-modules-commonjs": "^ 7.5.0"."@babel/plugin-transform-modules-systemjs": "^ 7.5.0"."@babel/plugin-transform-modules-umd": "^ 7.2.0"."@babel/plugin-transform-named-capturing-groups-regex": "^ 7.4.5"."@babel/plugin-transform-new-target": "^ 7.4.4." "."@babel/plugin-transform-object-super": "^ 7.5.5." "."@babel/plugin-transform-parameters": "^ 7.4.4." "."@babel/plugin-transform-property-literals": "^ 7.2.0"."@babel/plugin-transform-regenerator": "^ 7.4.5"."@babel/plugin-transform-reserved-words": "^ 7.2.0"."@babel/plugin-transform-shorthand-properties": "^ 7.2.0"."@babel/plugin-transform-spread": "^ 7.2.0"."@babel/plugin-transform-sticky-regex": "^ 7.2.0"."@babel/plugin-transform-template-literals": "^ 7.4.4." "."@babel/plugin-transform-typeof-symbol": "^ 7.2.0"."@babel/plugin-transform-unicode-regex": "^ 7.4.4." "."@babel/types": "^ 7.5.5." "."browserslist": "^ 4.6.0"."core-js-compat": "^ 3.1.1." "."invariant": "^ 2.2.2." "."js-levenshtein": "^ 1.1.3." "."semver": "^ 5.5.0"}}Copy the code

Babel plugin was mentioned in the last article. Now, all plugins in the proposal stage are named as plugins in the form of -proposal. Non-proposal plugins are now plugins of -transform form. Why does package.json above contain several -proposal plugins? This is because the plugin of the above -proposal has advanced to stage-4 by the time I write this article, and it will turn into the -trasform plugin sooner or later, so preset-env will include them.

Since the proposal changes constantly, meaning that preset-env will also be adjusted, keeping preset-env updated is an important task in normal projects.

Because of this, preset-env is not a panacea. If we use a new ES feature and are in the proposal phase, and preset-env does not provide transcoding support, we have to configure plugins ourselves.

Stage-x preset is no longer recommended, and preset-env does not support the stage-x plugin, but stage-2 is used in the past VUE project, meaning that some of the plugin in stage-2 is needed by the Vue project. How do you replace Stage-2 with a plugin configured separately? Babel official provides an alternative briefing file: How to replace Stag-X preset

7.browserslist

@babel/preset-env, a Browserslist needs to be configured like autoprefixer to confirm the target runtime environment. Although it is officially recommended that we use the same configuration as autoprefixer, with the help of a.browserslistrc file. Personally, I think browserslist should be configured separately in preset-env, because it’s not likely that other tools have exactly the same needs for the target environment as preset-env. Preset -env has a target option in its options, which can be used to configure browserslist for it alone.

Browserlist is configured by reading their official documentation.

NPX browserslist ‘iOS > 8, Android > 4’ :

> npx browserslist 'iOS > 8, Android > 4'
npx: installed 5 inIos_saf 12.2-12.3 IOS_SAF 12.0-12.1 Ios_saf 11.3-11.4 IOS_SAF 11.0-11.2 IOS_SAF 10.3 IOS_SAF 10.0-10.2 IOS_SAF 9.3 IOS_SAF 9.0-9.2 IOS_SAF 8.1-8.4Copy the code

8. options

  • target

Used to configure the target runtime environment.

string | Array | { [string]: string }, defaults to {}.
Copy the code

If target is a string, it should be written as a BrowserList query (what is a BrowserList query

{
  "target":"iOS > 8, Android > 4"
}
Copy the code

Target If is an object, browserList browsers can be configured directly on the object (what is BrowserList Browser), as follows:

{
  "target": {
    "iOs": "8"."Android": "4"}}Copy the code

This configuration of browser represents the lowest version of it.

In addition to the above two forms, targets can be configured in several other ways, but I do not think they are commonly used, so I will not go into detail about them. For details, please see

  • spec

This was mentioned in the previous article, the option is passed to the built-in plugin, and the spec is passed to preset if the plugin supports the option.

  • loose

This was mentioned in the previous article, the option is passed to the built-in plugin, and if the plugin supports the option, Loose will preset to it.

  • modules
"Amd" | "umd" | "systemjs" | "commonjs" | "CJS" | | "auto" false, defaults to "auto".Copy the code

This module configures whether to enable the conversion of ES6 modules to other specifications. In the VUE project, this option is displayed as false.

  • debug

This is used to enable transcoding debugging. I found it useful to see a lot of helpful hints, especially for polyfill related processing results.

  • corejs
2.3 or { version: 2 | 3.proposals: boolean }, defaults to 2.
Copy the code

This option specifies the version of Corejs to be used for polyfill of preset-env. Core-js is an unsupported browser environment written by a third party that also supports the latest ES feature library, which the authors call Standard Library. Core-js is currently in use in two versions: V2 and V3. So preset-env’s corejs option can support configuration 2 or 3. But from a future point of view, I don’t think core-JS V2 should be a concern, it will always be replaced by V3, and gradually everyone will upgrade to V3. So this article learns the polyfill behavior of Preset -env to Core-JS and ignores Core-js V2.

If only core-js V3 is considered, there are two configurations for the corejs option of preset-env:

corejs: 3
corejs: {version: 3.proposals: boolean}
Copy the code

By default, polyfills of Corejs will inject only those STABled ES features, while polyfills in the proposal state will not. If you need to inject polyfill of proposals, consider configuring corejs to: corejs: {version: 3, proposals: true}.

Corejs: {version: 3, proposals: true} are often used with the following useBuiltIns: ‘usage’.

  • useBuiltIns
"Usage" | | "entry"false, defaults to false.
Copy the code

Since @babel/polyfill started in Babel7.4, it is no longer supported. For preset-env, core-js V3 must be installed separately:

UseBuiltIns, which has two values: Entry and Usage. These two values, either way, inject core-js modules into the converted code, acting as polyfills. What is a Core-JS module? This requires a document specifically for core-JS, which I will also document in a later blog when I study core-JS.

  • entry

Let’s start by looking at how Entry works. Suppose. Babelrc.js is configured as follows:

const presets = [ 
    [
    "@babel/preset-env",
            {
                "targets": {
                    ios: 8.android: 4
                },
                useBuiltIns: "entry".corejs: 3.debug: true// Easy to debug}]].const plugins = [
];

module.exports = { presets, plugins };
Copy the code

Prepare the following code:

Promise.resolve().finally();

letobj = {... {}}; globalThis.obj = obj;Copy the code

Run NPX Babel SRC –out-dir dist to transcode it. Because of debug, the console prints:

Using polyfills with `entry` option:

[D:\babel\src\main.js] Import of core-js was not found.
Successfully compiled 1 file with Babel.
Copy the code

This prompts us to add an import call to core-js in our code to inject core-js as polyfill.

Modify the code as follows:

import "core-js"; Promise.resolve().finally(); let obj = {... {}}; globalThis.obj = obj;Copy the code

Run NPX Babel SRC –out-dir dist to transcode it. The Import of core-js was not found message is no longer displayed. The final transcoding results are as follows:

"use strict";

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

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

// Several hundred lines of require are omitted

require("core-js/modules/esnext.global-this");

require("core-js/modules/web.immediate");

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] ! =null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); }}return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true.configurable: true.writable: true }); } else { obj[key] = value; } return obj; }

Promise.resolve().finally();

var obj = _objectSpread({}, {});

globalThis.obj = obj;
Copy the code

This result is omitted and totals more than 500 lines. In useBuiltIns: Core-js import from code in ‘Entry’ mode: Import “core-js”, which is replaced by modules reference at the lowest level of core-js, such as require(“core-js/modules/es.symbol”), depending on the targets configuration. Core-js has many modules, although the source code is only one line import “core-js”, the converted code has over 500 lines require(“core-js/modules/…”) ).

If you want to continue using Babel/Polyfill in your project, just introduce the following code in the entry file:

import "core-js";
import "regenerator-runtime/runtime";
Copy the code

Is there a problem with this approach? Some may use too much polyfill, and some may not need it in the logic of the whole project. This method will generate large code, which will definitely affect the front-end performance. The only advantage is that you don’t have to worry about which file needs to introduce which Core-js modules as polyfills.

How can it be improved?

If you are familiar with the ES features used in the current file, you can choose to manually introduce core-js modules to avoid overall references. Change the code to:

import "core-js/es/promise";
import "core-js/es/array";

Promise.resolve().finally();

letobj = {... {}}; globalThis.obj = obj;Copy the code

Run NPX Babel SRC –out-dir dist to transcode it. The final result is:

"use strict"; require("core-js/modules/es.array.concat"); require("core-js/modules/es.array.copy-within"); / / save dozens of lines of the require (" core - js/modules/es. Array. Unscopables. Flat "); require("core-js/modules/es.array.unscopables.flat-map"); require("core-js/modules/es.object.to-string"); require("core-js/modules/es.promise"); require("core-js/modules/es.promise.finally"); require("core-js/modules/es.string.iterator"); require("core-js/modules/web.dom-collections.iterator"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] ! = null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } Promise.resolve().finally(); var obj = _objectSpread({}, {}); globalThis.obj = obj;Copy the code

This is still a lot, but much less than importing core-js directly. It works similarly by converting the import of core-JS to the smallest unit of core-JS: modules. :

import "core-js/es/promise";
import "core-js/es/array";
Copy the code

The two references above represent two core-JS namespaces: Promise and Array. They may contain multiple modules, so after transcoding, there are still dozens of require statements for core-JS modules.

While referring to portions of Core-JS alone can reduce the final transcoding size, developers are required to be familiar with the core-JS and ES features, otherwise how would you know if the current file needs polyfill, and which ones? So this approach, when you actually use it, is very difficult.

UseBuiltIns: “entry” replaces the import of core-js, which is determined based on the targets configuration environment. If you adjust the configuration file: (ios: 12 is a very new environment)

const presets = [ 
    [
    "@babel/preset-env",
            {
                "targets": {
                    ios: 12
                },
                useBuiltIns: "entry".corejs: 3.debug: true}]].const plugins = [
];

module.exports = { presets, plugins };
Copy the code

Then re-apply the following code:

import "core-js/es/promise";
import "core-js/es/array";

Promise.resolve().finally();

letobj = {... {}}; globalThis.obj = obj;Copy the code

Running Babel results in:

"use strict";

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

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

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

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

Promise.resolve().finally();
let obj = { ...{}
};
globalThis.obj = obj;
Copy the code

The same code, because targets is set to a newer version of the environment, so the final transcoding result is much reduced.

  • usage

The biggest advantage of Usage over Entry is that it determines whether to polyfill according to the targets that are set for the new ES feature in each file. If the targets minimum environment does not support an ES feature, The corresponding module of the ES feature’s core-JS will be injected.

Change the configuration file to:

const presets = [ 
    [
    "@babel/preset-env",
            {
                "targets": {
                    ios: 8.android: 4.1
                },
                useBuiltIns: "usage".corejs: 3.debug: true}]].const plugins = [
];

module.exports = { presets, plugins };
Copy the code

Change the code to:

Promise.resolve().finally();

letobj = {... {}}; globalThis.obj = obj;Copy the code

Run the NPX Babel SRC –out-dir dist transcode and the result is:

"use strict"; require("core-js/modules/es.symbol"); require("core-js/modules/es.array.filter"); require("core-js/modules/es.array.for-each"); require("core-js/modules/es.object.define-properties"); require("core-js/modules/es.object.define-property"); require("core-js/modules/es.object.get-own-property-descriptor"); require("core-js/modules/es.object.get-own-property-descriptors"); require("core-js/modules/es.object.keys"); require("core-js/modules/es.object.to-string"); require("core-js/modules/es.promise"); require("core-js/modules/es.promise.finally"); require("core-js/modules/web.dom-collections.for-each"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] ! = null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } Promise.resolve().finally(); var obj = _objectSpread({}, {}); globalThis.obj = obj;Copy the code

In the end, there are only so many transcoding results of this file, which is much simpler than using Entry. Moreover, it is very intelligent, and you do not need to worry about which polyfills you need. This is why Usage is recommended for projects.

Looking at the source code and transcoding, there is one small area that has not been converted: globalThis, an ES proposal currently in Stage-3, has not been injected into Polyfill. This is because preset-env does not polyfill proposals by default, so if you need to polyfill them, you can modify the configuration file a little bit:

const presets = [ 
    [
    "@babel/preset-env",
            {
                "targets": {
                    ios: 8.android: 4.1
                },
                useBuiltIns: "usage".corejs: {version: 3.proposals: true},
                debug: true}]].const plugins = [
];

module.exports = { presets, plugins };
Copy the code

NPX Babel SRC –out-dir dist transcode

"use strict"; require("core-js/modules/es.symbol"); require("core-js/modules/es.array.filter"); require("core-js/modules/es.array.for-each"); require("core-js/modules/es.object.define-properties"); require("core-js/modules/es.object.define-property"); require("core-js/modules/es.object.get-own-property-descriptor"); require("core-js/modules/es.object.get-own-property-descriptors"); require("core-js/modules/es.object.keys"); require("core-js/modules/es.object.to-string"); require("core-js/modules/es.promise"); require("core-js/modules/es.promise.finally"); require("core-js/modules/esnext.global-this"); require("core-js/modules/web.dom-collections.for-each"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] ! = null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } Promise.resolve().finally(); var obj = _objectSpread({}, {}); globalThis.obj = obj;Copy the code

Require (“core-js/modules/ esNext-global-this “); This is the polyfill of globalThis Proposal.

  • Other options

Other options, I suggest you need to learn.

That’s all there is to say about presets, there are a few more things that can be summarized from the official Babel documentation, but this takes too much time, and much information is scattered on the blog Github and preset’s separate introductory pages, which are hard to recall. This article covers the core points of [email protected] and plugins and preset are the most important knowledge points, which are enough to reinforce the basic configuration of Babel.

An excerpt: blog.liuyunzhuge.com/tags/babel/… Only for self-study use;