This article is the first in a series on using Babel.

What is Babel

Babel is a JavaScript compiler. Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

Babel can help us with the following:

  • Syntax conversion, ES6+ syntax into ES5 code
  • Polyfill is provided for the feature of the new version ES
  • TS/Flow support (type)
  • To generate the source map
  • .

Usage Guide

Babel can be used in the following ways:

  • Using the Cli
  • Use in code
  • With packaging tools (gulp, webpack, rollup)

plugins

Babel does nothing out of the box, meaning that a piece of ES6 code processed by Babel out of the box will output the same code as before. All of Babel’s transformations are done through Plugins, and each Plugin has its own ability to handle the corresponding ES code.

Example (out of the box) Installing Babel CLI and core

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

Write a simple line of code in SRC /index.js:

// src/index.js
const app = () => {};
Copy the code

Run Babel and output the finished files to the dist directory

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

Dist /index.js does not change after processing:

// dist/index.js
const app = () => {};
Copy the code

As of babel7, all NPM libraries provided by Babel are under the scope @babel.

Babel plugins fall into two categories:

  • Syntax class (Syntax)
  • Transform class

Syntax plugins have this to say on their website:

These plugins only allow Babel to parse specific types of syntax (not transform).

Therefore, Syntax Plugin is used to identify new Syntax without conversion. When the Transform Plugin is enabled, the corresponding Syntax will be automatically introduced without separate configuration.

plugins usage

Add some code to index.js:

const app = () => {}; class Person { constructor(name, age) { this.name = name; this.age = age; } getName() { return this.name; } getAge() { return this.age; }}Copy the code

Install the plugin:

npm install @babel/plugin-transform-arrow-functions @babel/plugin-transform-classes --save-dev
Copy the code

Create a configuration file for Babel (.babelrc.js)

const plugins = ['@babel/transform-arrow-functions', '@babel/transform-classes'];
module.exports = { plugins };
Copy the code

Use Babel again

function _classCallCheck(instance, Constructor) { if (! (instance instanceof 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; } const app = function () {}; let Person = /*#__PURE__*/function () { function Person(name, age) { _classCallCheck(this, Person); this.name = name; this.age = age; } _createClass(Person, [{ key: "getName", value: function getName() { return this.name; } }, { key: "getAge", value: function getAge() { return this.age; } }]); return Person; } ();Copy the code

You can see that both the class and arrow functions have been converted.

Order in which plugins are enabled

If both plugins were to process nodes of one type, in what order would they be processed? Babel processes the code in the following order:

  • The plugins before presets
  • The plugins array is processed internally from left to right
  • Presets are processed internally from right to left

transform-runtime

As you can see in the plugins example above, Babel introduces custom helper functions such as _createClass. The problem with this is that every file that uses the class has a piece of code that is identical. This duplication is not acceptable. So Babel provides the transform-Runtime plugin.

// shell
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime

// babel config
{
  "plugins": ["@babel/transform-runtime"]
}
Copy the code

In the converted code, you can see:

import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; var app = function () {}; var Person = /*#__PURE__*/function () { function Person(name, age) { _classCallCheck(this, Person); this.name = name; this.age = age; } _createClass(Person, [{ key: "getName", value: function getName() { return this.name; } }, { key: "getAge", value: function getAge() { return this.age; } }]); return Person; } ();Copy the code

So they’re all referring to the same code and there’s no duplication.

The Transform Runtime Plugin provides some configurations:

corejs

The default value is false and can be set to false, 2, or 3. Different values require different Babel/Runtime installations.

  • If false, Babel/Runtime needs to be installed
  • If the value is 2, you need to install Babel/Run-time corejs2
  • If the value is 3, you need to install Babel/Runtime-corejs3

What is the difference between the three values above? In the case of corejs: false, the transform-plugin only handles the syntax

const arr = [1, 2, 3]
arr.find(item => item > 2)
Copy the code

Babel handles the following:

var arr = [1, 2, 3];
arr.find(function (item) {
  return item > 2;
});
Copy the code

If corejs is set to 3 and run-time corejs3 is installed, Babel processes the following code:

import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find";
var arr = [1, 2, 3];
_findInstanceProperty(arr).call(arr, function (item) {
  return item > 2;
});
Copy the code

The difference between corejs: 2 and corejs: 3 is that 2 can only handle class methods, while 3 can handle both class and instance methods

presets

Babel provides presets to simplify the configuration of Babel. Official Presets:

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

Here are the main presets — env, and the other presets, as their name suggests, are very specific to handle certain preset situations.

@babel/preset-env

Env is the most complex of all presets, and preset, based on our configuration to Browserslist, is automatically converted to a more “smart” version during transcoding based on the lowest version of the target runtime environment of the transcoded code. If the ES feature to transcode has been implemented natively, the ES standard writing method will be used directly. If the feature to transcode is not yet supported in the lowest release environment, the corresponding Polyfill is automatically injected.

@babel/preset-env does not include all stage-x plugins. So 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.

How is preset-env used? Here are the daily configurations:

target

Tell env which target environment to be compatible with, as mentioned above, preset-env would be clever to help us with the code-conversion, and here is the lowest target environment to configure. Ex. :

{"targets": "> 0.25%, not dead"}Copy the code

modules

When setting the Babel conversion, you can set the following values for the module export mode:

  • amd
  • umd
  • systemjs
  • commonjs
  • cjs
  • auto
  • false

Example: Create a SRC /lib/index.js file

export const func = () => {}; export class Apple { constructor(price) { this.price = price; }}Copy the code

Without setting module, Babel:

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Apple = exports.func = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var func = function func() {};
exports.func = func;
var Apple = function Apple(price) {
  (0, _classCallCheck2["default"])(this, Apple);
  this.price = price;
};
exports.Apple = Apple;
Copy the code

Set module to false:

const presets = [['@babel/env', {modules: false}]];
Copy the code

After Babel processing, you can see that the module export mode is still es module:

import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
export var func = function func() {};
export var Apple = function Apple(price) {
  _classCallCheck(this, Apple);
  this.price = price;
};
Copy the code

useBuildIns

UseBuildIns can be set to Usage, Entry, false, or false by default. This property tells env how to polyfill because we’re going to polyfill some high-level syntax, class methods, instance methods, so we’re going to introduce core-js,

import "core-js";
Copy the code

Set useBuiltIns: “Entry”, and env will fetch the desired polyfills from it based on the current target environment, for example:

require("core-js/modules/es6.array.copy-within");
require("core-js/modules/es6.array.every");
require("core-js/modules/es6.array.fill");
require("core-js/modules/es6.array.filter");
require("core-js/modules/es6.array.find");
require("core-js/modules/es6.array.find-index");
require("core-js/modules/es7.array.flat-map");
require("core-js/modules/es6.array.for-each");
require("core-js/modules/es6.array.from");
// ...
Copy the code

You can see that Babel pulls a lot of polyfill files from Corejs depending on the current environment

Set useBuildIns to usage, and the current code uses methods that require polyfill, for example, using only the find method of the array

const arr = [1, 2, 3]
arr.find(item => item > 2)
Copy the code

After Babel processing, only find dependency will be introduced, not as many unused dependencies as entry.

require("core-js/modules/es6.array.find");
var arr = [1, 2, 3];
arr.find(function (item) {
  return item > 2;
});
Copy the code

corejs

The last configuration to cover is corejs. The version of Corejs to use for this property setting is 2 by default

When corejs is set to 3 and useBuildIns is usage, there is no need to manually import corejs at the code entry.

conclusion

Babel is a JS compiler. Out of the box, Babel does nothing. Plugins for Babel can transform syntax, and if polyfill is required, core-JS and Regenerator-Runtime should be used. UseBuildIns helps us polyfill better by setting presets-env.