The author:Jouryjc

What is Babel?

Babel is a toolchain for converting code written in ECMAScript 2015+ syntax to backward-compatible JavaScript syntax so that it can run in current and older browsers or other environments. We use Babel to do a few things:

  • Syntax conversion (es-higher -> es-lower);
  • Handle features that cannot be transformed in the target environment with a Polyfill (viacore-jsImplementation);
  • Source conversion (codemods,jscodeshift);
  • Static analysis (lint, based on commentsAPIDocuments, etc.);

Babel can do whatever he wants! 😎 😎 😎

Babel7 Minimum configuration

Did you ever feel “hurt by your first love” when you first played Babel? If so, check out this section, it will keep you hooked.

Before we taste the “taste of first love”, let’s make some preparations:

Create a new directory babel-test and create a package.json file:

mkdir babel-test && cdBabel-test // This article uses yarn init -yCopy the code

Install @babel/core, @babel/cli:

yarn add @babel/core @babel/cli -D
Copy the code

Everything is ready, now just buy a plate of 🌰, you can hold her hand:

// Create a new index.js file in the root directory and type 🌰 below
let{ x, y, ... z } = {x: 1.y: 2.a: 3.b: 4 };
console.log(x); / / 1
console.log(y); / / 2
console.log(z); // { a: 3, b: 4 }
Copy the code

First love, you blush when you meet their hair, and you want to see their reaction, right? So execute the following command and see what happens:

NPX Babel./index.js --out-file build.jsCopy the code

After executing the above command, it will output a build.js file in the root directory.

let{ x, y, ... z } = {x: 1.y: 2.a: 3.b: 4
};
console.log(x); / / 1

console.log(y); / / 2

console.log(z); // { a: 3, b: 4 }

Copy the code

What the xxx? This ** isn’t just formatting! A run on IE10, another sign of sleepless nights:

Is it surprising or not? Caniuse a check, I nima, which * force to use the extension operator ah, do not know that we should be compatible with IE ah!

But as fierce pursuers, how can we give up just because the other side recoiled! Go to the Babel plugins page and see what plugins you need to handle the extension operator – you can see that this is an ES2018 feature available with the @babel/ plugin-proposal-object-Rest-spread plugin.

Blunt! Hold out your dark hand once more. Create a file named babel.config.json (.babelrc or babel.config.js, depending on your scenario) in the project root directory (where package.json files are located). For the file, see Configuring Babel) and enter the following:

// Go to the terminal and run yarn add@babel /plugin-proposal-object-rest-spread -d to install the dependency first
{
    "plugins": ["@babel/plugin-proposal-object-rest-spread"]}Copy the code

Then execute:

npx babel ./index.js --out-file build.js
Copy the code

Then open the build.js file again, and you can see that the extension operator is no longer visible:

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; }}return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

let _x$y$a$b = {
  x: 1.y: 2.a: 3.b: 4
},
    {
  x,
  y
} = _x$y$a$b,
    z = _objectWithoutProperties(_x$y$a$b, ["x"."y"]);

console.log(x); / / 1

console.log(y); / / 2

console.log(z); // { a: 3, b: 4 }

Copy the code

Refresh Internet Explorer and open F12 to see the debugger panel:

She withdrew her hand again. Did it hurt? But as a man wearing a red scarf and a small red flower on his head, I will never be discouraged! Look at the location of the error code and recognize that IE does not even support deconstructive assignments. For the same procedure, check caniuse and @babel/plugin-transform-destructuring (tip: click on it to go to the corresponding page!).

This time, go hold her hand again, gogogo:

// Go to the terminal and enter yarn add@babel /plugin-transform-destructuring -d to install dependencies first
{
    "plugins": [
        "@babel/plugin-proposal-object-rest-spread"."@babel/plugin-transform-destructuring"]}Copy the code

After the installation, compile it again, and you can see the generated code as follows:

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; }}return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

let _x$y$a$b = {
  x: 1.y: 2.a: 3.b: 4
},
    x = _x$y$a$b.x,
    y = _x$y$a$b.y,
    z = _objectWithoutProperties(_x$y$a$b, ["x"."y"]);

console.log(x); / / 1

console.log(y); / / 2

console.log(z); // { a: 3, b: 4 }

Copy the code

Look again at IE’s response:

God waits, IE became, you also hand in hand success!

If you’re careful, both of these Babel plugins have a NOTE:

NOTE: This plugin is included in @babel/preset-env

What does that mean? The girl say hope you next time courage again big point, can pull on and then do not put, why want to try many times!

This leads to @babel/ presets -env, and the document uses the presets field of the configuration file on the wrapper. Then remove the first two plugins.

yarn remove @babel/plugin-transform-destructuring @babel/plugin-proposal-object-rest-spread

yarn add @babel/preset-env -D
Copy the code

Babel.config. json:

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

Then execute the build command again, and you can see that the output of the build.js file is the same!

And wonder at the same time:

  • Why does a preset satisfy the conversion requirement? How does it do that?
  • BabelHow do you know I want to support itIEBrowser, if I only useChrome, isn’t this conversion redundant? Babel is not only used in browsers, but also in desktop and Node scenarios. How does Babel precisely control the conversion?

Before answering the above question, it occurred to me that while reviewing code at my company, I saw that many children’s shoes were dominated by TypeScript in order to use TypeScript (for example, where AnyScript got its name). Hope to think from the birth background and function of technology, tools and framework itself! Why use it if you don’t have more elaborate type declarations and restrictions?

Babel6 to Babel7:

  • abandonedstage-xes20xxpresetTo change topreset-envplugin-proposal-xx, the specific proposal information is inTC39/proposalsConsult, which gives you better control over the features you need to support;
  • Preset -env relies on Browserslist, Compat-table, and electron-to- Chromium to implement the refined on demand introduction of the feature.

compat-table

This library maintains support for each feature in different environments. Take a look at the deconstructed assignment support used above:

{
  name: 'destructuring, declarations'.category: 'syntax'.significance: 'medium'.spec: 'http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-assignment'.mdn: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment'.subtests: [{name: 'with arrays'.exec: function(){/* var [a, , [b], c] = [5, null, [6]]; return a === 5 && b === 6 && c === void undefined; * /},
      res: {
        tr: true.babel6corejs2: babel.corejs,
        ejs: true.es6tr: true.jsx: true.closure: true.typescript1corejs2: true.firefox2: true.opera10_50: false.safari7_1: true.ie11: false.edge13: edge.experimental,
        edge14: true.xs6: true.chrome49: true.node6: true.node6_5: true.jxa: true.duktape2_0: false.graalvm19: true.graalvm20: true.graalvm20_1: true.jerryscript2_0: false.jerryscript2_2_0: true.hermes0_7_0: true.rhino1_7_13: true}}Copy the code

Ie11 is false, no wonder the first hand failed.

browserslist

This package should be familiar enough that you can query the specific list of browsers. Let’s install this package and run with it:

Yarn add browserslist -d // The query conditions are different. Specific reference https://github.com/browserslist/browserslist#queries NPX browserslist "> 0.25%, Not dead" and_CHR 91 and_FF 89 and_UC 12.12 Android 4.4.3-4.4.4 Chrome 91 Chrome 90 Chrome 89 Chrome 87 Chrome 85 Edge 91 Firefox 89 Firefox 88 IE 11 IOS_SAF 14.5-14.7 iOS_SAF 14.0-14.4 iOS_SAF 13.4-13.7 op_mini all Opera 76 Safari 14.1 Safari 14 Safari 13.1 Samsung 14.0 Samsung 13.0Copy the code

With the above two packages, the implementation of preset-env features fine control is not sprinkling water. Let’s go ahead and change the starting 🌰 to no need to support IE11 and try:

{
  "presets": [["@babel/preset-env",
        {
    		"targets": {
                "chrome": 55}}]]}Copy the code

Preset -env Controls the browser version by configuring the targets field. Build it and see the results:

"use strict";

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; }}return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

let _x$y$a$b = {
  x: 1.y: 2.a: 3.b: 4
},
    {
  x,
  y
} = _x$y$a$b,
    z = _objectWithoutProperties(_x$y$a$b, ["x"."y"]);

console.log(x); / / 1

console.log(y); / / 2

console.log(z); // { a: 3, b: 4 }

Copy the code

As you can see from the source code above, the extension operator is converted, but the destruct assignment is not. The properties of the converted by module _objectWithoutProperties and _objectWithoutPropertiesLoose defines two methods. What if I have two files that use the extension operator and then output one file? Create a new index2.js file in the root directory:

let{ x, y, ... z } = {x: 1.y: 2.a: 3.b: 4 };
console.log(x); / / 1
console.log(y); / / 2
console.log(z); // { a: 3, b: 4 }
Copy the code

Then execute the following two commands:

npx babel ./index.js ./index2.js --out-file build.js
Copy the code

The result is _objectWithoutProperties and the declaration of _objectWithoutPropertiesLoose incredibly is repeated twice. This is a feature that needs to be converted, and I use it a lot. Doesn’t the converted output explode? At this point you need a plugin to control the amount of code — @babel/ plugin-transform-Runtime. For this conversion function, in the external modularity, the use of the place can be directly introduced. Speaking:

// Install the @babel/plugin-transform-runtime package yarn add @babel/plugin-transform-runtime -d firstCopy the code

Then configure Babel:

{
    "presets": [["@babel/preset-env",
            {
                "targets": {
                    "chrome": "55"}}]],"plugins": [
        "@babel/plugin-transform-runtime"]}Copy the code

Executing the build command above yields the following results:

"use strict";

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

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

let _x$y$a$b = {
  x: 1.y: 2.a: 3.b: 4
},
    {
  x,
  y
} = _x$y$a$b,
    z = (0, _objectWithoutProperties2.default)(_x$y$a$b, ["x"."y"]);
console.log(x); / / 1

console.log(y); / / 2

console.log(z); // { a: 3, b: 4 }

let _a$b$c = {
  a: 1.b: 2.c: 3
},
    {
  a,
  b
} = _a$b$c,
    c = (0, _objectWithoutProperties2.default)(_a$b$c, ["a"."b"]);

Copy the code

Compared to the above conversion results, this conversion result is much simpler. And function declarations are imported externally.

Take a look at the following code:

const a = [1.2.3.4.6];
console.log(a.includes(7))
Copy the code

@babel/compat-data shows the compatibility of includes:

"es7.array.includes": {
    "chrome": "47"."opera": "34"."edge": "14"."firefox": "43"."safari": "10"."node": "6"."ios": "10"."samsung": "5"."electron": "0.36"
}
Copy the code

Chrome 47+ supports the array includes API. We change targets in babel.config.json to 45 and then execute the conversion command.

"use strict";

var a = [1.2.3.4.6];
console.log(a.includes(7));
Copy the code

It can be concluded that while array.prototype. inlcudes is not supported, Babel does not convert instance methods by default. This is where the @babel/ Polyfill patch comes in. (⚠️ installing polyfill is dependency! In a production environment, the shim is executed before your code.

Import all polyfills in a project entry file or in an entry packaging tool such as WebPack:

// app.js
import '@babel/polyfill';

// webpack.config.js
module.exports = {
  entry: ["@babel/polyfill"."./app/js"]};Copy the code

We don’t need many of these features, so can we also use broswer Targets and the functions used in the code to customize the shims? Choose your own shim and generate a CDN URL. It can be directly introduced in the project, which can be used for micro websites, for large projects, it is impossible to choose a method of their own. This leads to the useBuiltIns configuration, which defines what @babel/preset-env does with washers. The optional values are:

  • usage: Features referenced by each file;
  • entry: All entrances are introduced;
  • false: Do not introduce.

// index.js
const a = [1.2.3.4.6];

console.log(a.includes(7))

new Promise(() = > {})
Copy the code

Then change the Babel configuration to the following:

{
    "presets": [["@babel/preset-env",
            {
                "targets": {
                    "chrome": "45"."ie": 11
                },
                "useBuiltIns": "usage"."corejs": 3}]],"plugins": [
        "@babel/plugin-transform-runtime"]}Copy the code

Babel./index.js –out-file build.js

"use strict";

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

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

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

var a = [1.2.3.4.6];
console.log(a.includes(7));
new Promise(function () {});

Copy the code

Niubility!!! Specific core-JS spacers have been introduced for unsupported features. How does this work? Again, this is thanks to the AST, which allows you to make super detailed, on-demand references to the actual situation in your code. If you are interested in children’s shoes, check out the way Core-JS and Babel work together.

summary

Through 🌰 to analyze the Babel7 minimum optimal configuration generation step by step, which also involves some write configuration in the non-aware processing mechanism, such as compat-table, Browserslist. After reading this section, you should have a clear understanding of babel7 configuration methods.

@ Babel series package

Babel is a Monorepo project with 146 packages under packages. Has conjured! Although there are many packages, we can divide them into several categories:

@babel/helper-xx has 28 and @babel/plugin-xx has 98. There are only 20 toolkits and integration packages left. Let’s pick some interesting packages to see what they do.

@babel/standalone

Babel-standalone Provides a standalone build of Babel for browsers and other non-Node environments, such as the online IDE: JSFiddle, JS Bin, and Babel’s official try It Out are all based on this package. Let’s play, too

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>standalone</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
    <div id="app"></div>
    <script type="text/babel">
        const codeStr = Const getMessage = () => "Babel, do whatever you want "; `;
        const code = Babel.transform(codeStr, { presets: ["env"] }).code;
        document.querySelector('#app').innerHTML = code;
    </script> 
</body>
</html>
Copy the code

Run the above code directly in the browser, and you get the following code:

"use strict"; var getMessage = function getMessage() { return "Babel, do whatever you want."; };
Copy the code

The result is the same as if it were built in the Node environment.

@babel/plugin-xx

Anything that satisfies this tag is a Babel plug-in. It is mainly used to enhance the transform and parser capabilities. An 🌰 :

// index.js
const code = require("@babel/core").transformSync(
    `var b = 0b11; var o = 0o7; const u="Hello\\u{000A}\\u{0009}!" ; `
).code;

console.log(code)
Copy the code

Run node index.js and return:

var b = 0b11;
var o = 0o7;
const u = "Hello\u{000A}\u{0009}!";
Copy the code

Returning it as is, if I want to recognize binary integers, hexadecimal integers, Unicode string literals, newlines, and tabs, I need to add @babel/plugin-transform-literals. The execution result is as follows:

var b = 3;
var o = 7;
const u = "Hello\n\t!";
Copy the code

Through the above Demo, you can understand the function of plugin.

Opening Babel/Packages, we can see that there are three main types of plugins:

  1. babel-plugin-transform-xx: Conversion plug-in, mainly used to enhance the conversion capability, above@babel/plugin-transform-literalsIt belongs to this;
  2. babel-plugin-syntax-xxSyntax plugins that extend compilation capabilities, such as using await outside async function scope, if not introduced@babel/plugin-syntax-top-level-awaitThere is no way to compile toASTOf the tree. And will be submitted to theUnexpected reserved word 'await'This kind of mistake.
  3. Babel-plugin-proposal-xx: Used to compile and transform properties in a proposal. You can see these plug-ins in the Plugins List, such as class-properties and decorators.

conclusion

Babel/Standalone is an interesting collection of packages and plugins that you can use to standalone /standalone. The next article will dive into the underlying packages with the hand-written Babel Plugin, such as @babel/core, @babel/ Parser, @babel/ Generator, and @babel/code-frame.