In recent years, if you were a front-end developer, if you didn’t use or even heard of Babel, you might be considered a time-traveler.

When you say Babel, a bunch of words pop up:

  • babel-cli
  • babel-core
  • babel-runtime
  • babel-node
  • babel-polyfill
  • .

Are these all Babel? What do they do respectively? Is there a difference?

What the hell did Babel do? How did you do that?

Simply translate the new JavaScript syntax of ES2015/2017/2046 into ES5 and make it accessible and executable in low end environments such as browsers and Nodes. This article uses Babel 6.x as a benchmark for discussion. Babel recently released 7.x, which we’ll talk about at the end.

Strictly speaking, Babel can also be converted to a lower specification. But as it stands now, the ES5 specification is sufficient to cover most browsers, so it is generally safe and popular to move to ES5.

If you’re not familiar with ES5 / ES2015, etc., then you might really need to make up for it first.

Method of use

There are three ways:

  1. Use a standalone script
  2. The command line (cli)
  3. Build tool plug-ins (babel-loader for Webpack, rollup-plugin-babel for rollup).

The latter two are more common. The second is found in a command in the scripts paragraph of package.json. The third is directly integrated into the build tool.

The only difference between these three methods is the entry point, and the Babel kernel that you call handles it the same way, so let’s not worry about the entry point.

Runtime and plug-ins

Babel is divided into three phases: parsing, transformation, and generation.

Babel itself does not have any transformation functions, and it breaks them up into plugins. So when we don’t configure any plug-ins, the code that goes through Babel is the same as the input.

There are two types of plug-ins:

  1. When we added the syntax plug-in, the parsing step enabled Babel to parse more syntax. (Incidentally, Babel didn’t develop the internal parsing library called Babylon.)

For a simple example, when we define or call a method, we are not allowed to add a comma after the last parameter, such as callFoo(param1, param2,) is illegal. If the source code is written this way, it passes through Babel with a syntax error.

But recent JS proposals have allowed this new approach (to make the code diff clearer). To avoid Babel errors, add the syntax plug-in babel-plugin-syntax-trailing-function-commas

  1. When we add the translation plug-in, we convert and export the source code in the transform step. This is the very essence of Babel.

Translation plugins are better understood than syntax plugins, such as the arrow function (a) => a is converted to function (a) {return a}. The plug-in that does this is called babel-plugin-transform-es2015-arrow-Functions.

There may be both a syntax plug-in version and a translation plug-in version for the same type of syntax. If we use the translation plug-in, we do not need to use the syntax plug-in.

The configuration file

Since plug-ins are fundamental to Babel, how do you use them? There are two steps in total:

  1. Add the name of the plug-in to the configuration file (create.babelrc or package.json in the root directorybabelInside, same format)
  2. usenpm install babel-plugin-xxxTo install

The specific writing format will not be detailed.

preset

Es2015, for example, is a set of specifications that includes about a dozen or twenty translation plug-ins. If developers had to add and install them one at a time, the configuration files would be long and NPM would take a long time to install, not to mention that we might have to use other specifications at the same time.

To solve this problem, Babel also provides a collection of plug-ins. Because it is commonly used, you do not have to repeat the definition & installation. (The difference between a la carte and a set meal, which saves a lot of time and configuration effort)

Preset is classified into the following types:

  1. Env, React, Flow, Minify, etc. The most important thing here is env, more on that later.

  2. Stage-x, which contains the draft of the latest specification of the year, is updated every year.

    This is subdivided into

    • Stage 0 – Scarecrow: Just an idea, put forward by TC39 members.
    • Stage 1 – Proposal: A preliminary attempt.
    • Stage 2 – First Draft: Complete the preliminary specification.
    • Stage 3 – Candidate: Complete specification and preliminary browser implementation.
    • Stage 4 – Complete: will be added to next year’s release.

    For example, syntax-dynamic-import is stage-2, transform-object-rest-spread is stage-3.

    In addition, the lower stage contains all the contents of the higher stage. For example, stag-1 contains all the contents of stag-2 and stag-3.

    Stage-4 will be put directly into env in the next update, so there is no separate stage-4 available.

  3. es201x, latest

    These are grammars that have been incorporated into the standard specification. For example, ES2015 contains arrow-functions and ES2017 contains syntax-trailing-function-commas. However, both ES2016 and ES2017 have been deprecated due to env. So we often see ES2015 listed separately, but rarely see the other two.

    Latest is the prototype of env, an annual update of preset, intended to encompass all ES201X. But also because of the more flexible Env, it has fallen into disuse.

Execution order

A few simple principles:

  • Plugin will run ahead of Preset.
  • Plugins are executed from front to back.
  • Preset is reversed (back-to-front).

The reverse order of preset is mainly to ensure backward compatibility, as most users write in the order [‘ ES2015 ‘, ‘stage-0’]. Stage-0 must be executed to ensure that Babel does not report errors. Therefore, we should also pay attention to the sequence when scheduling preset, which is just listed in the normal chronological order.

Configuration items for plug-ins and Preset

In shorthand, plug-ins and preset just list the name of the string format. But if a preset or plug-in needs some configuration items (or parameters), it needs to turn itself into an array first. The first element is still a string, representing its name; The second element is an object, the configuration object.

Env needs to be configured most, as follows:

"presets": [
    // With configuration items, it becomes an array
    [
        // The first element is still the name
        "env".// The second element is the object that lists the configuration items
        {
          "module": false}].// List names without configuration items
    "stage-2"
]
Copy the code

Env (emphasis)

Env is the most commonly used and important, so it’s worth focusing on.

The core purpose of env is to learn the characteristics of the target environment through configuration, and then make only the necessary transformations. For example, THE target browser supports ES2015, es2015 preset is not needed, so the code can be smaller (converted code is always longer) and the build time can be shorter.

If no configuration items are written, env is equivalent to Latest and is equivalent to the sum of ES2015 + ES2016 + ES2017 (excluding stage-x plug-ins). The list of plug-ins included with env is maintained here

The following are some common configuration methods:

{
  "presets": [["env", {
      "targets": {
        "browsers": ["last 2 versions"."safari >= 7"}}]]}Copy the code

The above configuration takes into account the features of the latest 2 versions of all browsers (Safari 7.0 or higher) and converts the necessary code. The existing functionality of these versions is not translated. See Browserslist for syntax here

{
  "presets": [["env", {
      "targets": {
        "node": "6.10"}}}]]Copy the code

The above configuration sets the target to nodeJS and supports versions 6.10 and later. You can also use Node: ‘current’ to support the latest stable version. For example, arrow functions will not be converted on nodeJS 6 and above, but will be converted on NodeJS 0.12.

Another useful configuration item is modules. It can be amd, UMD, SystemJS, CommonJS, or false. This allows Babel to output code in a specific modular format. If false is selected, modularization is not done.

Other supporting tools

The core processing mechanism and configuration methods of Babel have been discussed above, and Babel is invoked from any point of entry. But the Babel -* at the beginning of the article is still confusing. In fact, most of these Babel -* are different entrances (ways) to use Babel, which is briefly described below.

babel-cli

As the name implies, the CLI is a command line tool. With babel-CLI installed, you can compile files using the Babel command from the command line.

The following patterns are often used when developing NPM packages:

  • thebabel-cliFor the installation ofdevDependencies
  • Add it in package.jsonscripts(e.g.,prepublish), usingbabelCommand compiled file
  • npm publish

This allows you to write source code using the JS syntax of the newer specification while supporting older environments. Since the project may not be large enough to use a build tool (Webpack or rollup), use Babel-CLI before publishing.

babel-node

Babel-node is part of babel-CLI and does not require a separate installation.

It runs es2015 code directly from the Node environment without additional transcoding. For example, we have a JS file written in ES2015 syntax (such as using arrow functions). We can use babel-node es2015.js to execute without transcoding.

Babel-node = babel-polyfill + babel-register And who are these two?

babel-register

The babel-register module overwrites the require command by adding a hook to it. Thereafter, whenever files with.js,.jsx,.es, and.es6 suffixes are loaded using require, Babel is transcoded first.

To use it, require(‘babel-register’) must be loaded first.

Note that babel-register will only transcode the files loaded by the require command, not the current file.

Also, because it is real-time transcoding, it is only suitable for use in development environments.

babel-polyfill

By default, Babel only converts JS syntax, not new apis, such as Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise, etc. And some methods defined on global objects (such as Object.assign) do not transcode.

For example, ES2015 adds the array. from method to Array objects. Babel doesn’t transcode this method. If you want this method to work, you must use babel-Polyfill. (Internal integration of Core-JS and Regenerator)

When used, add require(‘babel-polyfill’) before all code runs. Or, more generally, babel-polyfill as the first entry in webpack.config.js. Therefore, you must refer to babel-polyfill as dependencies instead of devDependencies

Babel-polyfill has two major disadvantages:

  1. Using Babel-Polyfill results in a very large package because babel-Polyfill is a whole, adding all the methods to the prototype chain. For example, we only use array. from, but it also adds Object.defineProperty, which is a waste. This problem can be solved by using one of the core-JS libraries in isolation. Core-js is separate.

  2. Babel-polyfill contaminates global variables and makes changes in the prototype chain for many classes, which can become very uncontrollable if we develop a library for other developers to use.

So in practice, if we can’t tolerate these two drawbacks (especially the second one), we usually prefer babel-plugin-transform-Runtime.

However, if the code contains instance methods of older JS types (such as [1,2,3].includes(1)), polyfill is still used.

Babel-runtime and babel-plugin-transform-Runtime

We often see babel-plugin-transform-runtime used in.babelrc projects, Package. json dependencies include babel-Runtime dependencies. What role do they play?

The first said Babel – plugin – transform – runtime.

Babel transforms JS syntax, as mentioned earlier. Using async/await as an example, if this plugin is not used (which is the default), the transformed code would look like this:

// Babel adds a method to convert async to a generator
function _asyncToGenerator(fn) { return function () {....}} // A long, long time

// Specific usage
var _ref = _asyncToGenerator(function* (arg1, arg2) {
  yield (0, something)(arg1, arg2);
});
Copy the code

Without worrying too much about the syntax, just see that this _asyncToGenerator is defined in the current file and then used instead of await the source code. But each converted file inserts a _asyncToGenerator which leads to duplication and waste.

After using babel-plugin-transform-Runtime, the transformed code becomes

// Change the definition from direct to reference, so that the definition is not repeated.
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

// The exact usage is the same
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
  yield (0, something)(arg1, arg2);
});
Copy the code

By changing the definition method to a reference, the repeated definition becomes the repeated reference, and there is no problem with code duplication.

But here we also find that babel-Runtime is a collection of these methods, so we must use babel-Runtime as a dependency when using babel-plugin-transform-Runtime.

And babel-Runtime, it’s integrated internally

  1. Core-js: Converts some built-in classes (Promise, Symbols, etc.) to static methods (array. from, etc.). Most of the conversion is done here. Automatic import.

  2. Regenerator: Core-JS add-on, mainly generator/yield and async/await groups. Generators /async are automatically introduced when code has generators/async.

  3. Helpers, such as asyncToGenerator, JSX, classCallCheck, babel-helpers. Remove definitions and insert references when you use helpers built-in in your code (the first section above).

Babel-plugin-transform-runtime does not support instance methods (e.g. [1,2,3].includes(1))

Babel-plugin-external-helpers is a plugin that allows you to take helpers apart and combine them to avoid repeating code. But because the transform-Runtime we used already includes this functionality, we don’t have to reuse it. Babel’s authors have also begun to discuss how similar the two plugins are, and are discussing removing external-helpers in Babel 7, in issue#5699.

babel-loader

Three ways to use Babel were mentioned earlier, and babel-CLI was introduced. However, some large projects have build tools (such as WebPack or rollup) for code build and compression (UGlify). Theoretically, we could Babel the compressed code as well, but that would be very slow. So wouldn’t it be perfect to add Babel processing before Uglify?

Hence the need for Babel to be inserted into the build tool. Take the (somewhat familiar) Webpack for example. Webpack has the concept of loader, hence babel-loader.

Like babel-CLI, babel-loader reads the Babel segment in.babelrc or package.json as its own configuration, and subsequent kernel processing is the same. The only thing more complicated than Babel-CLI is that it needs to interact with Webpack, so it needs to be configured on the Webpack side. The more common ones are as follows:

module: {
  rules: [{test: /\.js$/.exclude: /(node_modules|bower_components)/.loader: 'babel-loader'}}]Copy the code

If you want to pass in the Babel configuration item here, you can also change it to:

// loader: 'babel-loader' :
use: {
  loader: 'babel-loader'.options: {
    // The configuration item is here}}Copy the code

The configuration item has the highest priority. But I think it’s clearer and more readable to put it in a separate configuration file.

The subtotal

The name of the role note
babel-cli Allows the command line to translate files using the Babel command
babel-node Allows the command line to directly translate and execute node files using babel-node Along with thebabel-cliinstalled

babel-node = babel-polyfill + babel-register
babel-register rewriterequireCommand to transcode the loaded file without transcoding the current file Only applicable to development environments
babel-polyfill Add compatibility methods for all apis Need to be before all the coderequireAnd the volume is relatively large
babel-plugin-transform-runtime & babel-runtime Change the help class methods from defined before each use to unifiedrequire, simplify code babel-runtimeYou need to install dependencies, not develop them
babel-loader Use Webpack as a loader for code conversion before code obfuscations

Babel 7.x

Babel recently released 7.0. Since the above is written for 6.x, let’s focus on the changes that 7.0 will bring (no changes in the core mechanics, plug-ins, preset, parsed translation generation).

I’ve listed only the ones that are most relevant to the developer, most of the ones omitted are changes to a plugin. The full list can be found on the official website.

Preset changes: phase-out ES201x, delete stag-x, push env (emphasis)

The purpose of phasing out ES201X was to leave the choice of environment to env automatically, without requiring developer input. Anyone using ES201x should use env instead. Babel 8 will be deprecated in the future because it will be deprecated in the future.

Stage-x, by contrast, didn’t have the same luck; they were simply deleted. This is because the Babel team thought it was a waste of effort to update Preset for these “unstable drafts”. Stage-x has been removed, but the plug-ins it contains have not been removed (they have just been renamed, as you can see in the next section), and we can still declare them explicitly to get the equivalent effect. A complete list

To reduce the mechanical work of replacing configuration files for developers, Babel developed a tool called Babel-upgrade that detects stage-x in Babel configurations and replaces them with plugins. It also has other functions, which we’ll look at in more detail in a moment. (The goal is to make your migration to Babel 7 smoother.)

NPM Package name changes (emphasis)

This is a major change to Babel 7, renaming all Babel -* to @babel/*, for example:

  1. babel-cliTurned out to be@babel/cli.
  2. babel-preset-envTurned out to be@babel/preset-env. Furthermore, it can be omittedpresetShorthand for@babel/env.
  3. babel-plugin-transform-arrow-functionsTurned out to be@babel/plugin-transform-arrow-functions. andpresetThe same,pluginI can also omit it, so I’ll just write it@babel/transform-arrow-functions.

This change applies not only to package.json dependencies, but also to.babelrc configurations (plugins, presets) for consistency. For example,

{
  "presets": [
- "env"
+ "@babel/preset-env"]}Copy the code

Incidentally, the kernel of Babel parsing syntax mentioned above has been renamed “Babylon” to @babel/ Parser, which seems to have been incorporated.

The stage-x mentioned above has been removed, and the plug-ins it contained, while retained, have also been renamed. The Babel team wanted a clearer distinction between plug-ins already in the specification (such as babel-plugin-transform-arrow-Functions in ES2015) and those only in the draft (such as stage-0) @ Babel/plugin – proposal – function – bind). The way to do this is to add proposal to the name, which is used by all translation plug-ins included in stage-x, without syntax plug-ins.

Finally, if the plug-in name contains the specification name (-ES2015 -, -ES3 -, etc.), delete it. For example, babel-plugin-transform-es2015-classes becomes @babel/plugin-transform-classes. (I have not used this plug-in alone, shame)

Nodes of earlier versions are no longer supported

Babel 7.0 no longer supports nodeJS versions 0.10, 0.12, 4, and 5, which is equivalent to requiring nodeJS >= 6 (currently nodeJS LTS is 8, which is not too much to ask).

By unsupported, I mean that you can’t use Babel code in these lower-version Node environments, but Babel code will still run in those environments, not to be confused.

Changes of only and ignore matching rules

In Babel 6, ignore, if it contains *.foo.js, actually means (glob)./**/*.foo.js, which means that the current directory includes all foo.js ending files in subdirectories. This may go against the conventional wisdom of developers.

So in Babel 7, the same expression *.foo.js works only on the current directory, not on subdirectories. If you still want to use subdirectories, you should write them as./**/*.foo.js according to the full specification of Glob. Only is the same.

This rule change applies only to wildcards, not paths. So node_modules still contains all of its subdirectories, not just one layer. (Otherwise developers all over the world will explode)

@babel/node is separate from @babel/cli

Unlike Babel 6, if @babel/node is to be used, it must be installed separately and added to dependencies.

babel-upgrade

This tool was mentioned in the context of removing stage-x, which is intended to help users automatically upgrade from Babel 6 to 7.

The features of this upgrade tool include :(the complete list is not included here, only the important and frequently used ones)

  1. package.json
  • Put all of the dependencies (and development dependencies) inbabel-*Replace with@babel/*
  • Put these@babel/*Dependent versions are updated to the latest version (for example^ 7.0.0)
  • ifscriptsUse ofbabel-node, automatically add@babel/nodeFor developing dependencies
  • If you havebabelConfiguration items, check thepluginspresets, the short name (env) with the full name (@babel/preset-env)
  1. .babelrc
  • Examine thepluginspresets, the short name (env) with the full name (@babel/preset-env)
  • Check for inclusionpreset-stage-x, if any, replace with the corresponding plug-in and add toplugins

The usage is as follows:

# NPM is a new feature that runs commands directly instead of installing them locally
npx babel-upgrade --write

# or the normal way
npm i babel-upgrade -g
babel-upgrade --write
Copy the code

The Babel-Upgrade tool itself is still under development and lists a lot of unfinished TODO, so there may be more features to come, such as the ignore wildcard conversion mentioned above.