background

When it comes to ES6, Webpack, packaging, and modularity, Babel is the most widely used js compiler. Babel’s website describes it like this:

Babel is a JavaScript compiler.

Use next generation JavaScript, today.

As we all know, JS as a host language is very dependent on the environment (browser, node, etc.), different environments for THE support of JS syntax is not the same, especially after ES6, ECMAScrip version of the update has been to the pace of a year, although the annual update range is not large, but the annual proposal can be many. Babel was created to solve this problem, translating code written using the new standard into code that works in the current environment, or simply translating (transcoding + compiling) ES6 code into ES5.

To write ES6, you need to introduce a Babel-Loader in webPack and copy it on the Internet. You can start by going to the project directory and putting babelrc into the project directory (ps: I mean myself). It is important to understand the configuration of Babel to avoid some pitfalls, such as the use of object. assign in your code, which some earlier versions of the browser will report, thinking that there is a webpack problem, but is actually a Babel configuration problem.

ES6

ES6 is ECMAScript. 6 stands for version 6 (also known as ES2015 because it was released in 2015), which is the implementation standard for javascript.

Grammar to be incorporated into the ES standard must go through five stages:

  1. Stage 0: strawman
  2. Stage 1: proposal
  3. Stage 2: Draft – Must contain two experimental concrete implementations, one of which can be implemented with a translator, such as Babel.
  4. Stage 3: Candidate – There must be at least two concrete implementations that conform to the specification.
  5. Stage 4: finished

You can see that the proposal was already implemented in some environments when it entered Stage3, and there was an implementation of Babel in StagE2. So the syntax that is incorporated into the ES standard is already implemented in most environments, so why use Babel for translation when there is no guarantee that every environment where the code is running is up to date and has implemented the specification.

For more on ES6, see Hax’s Live: HaX: How to Learn and Practice ES201X?

Babel version change

As of this writing, Babel has reached V7.0.0-bebeta.3, which means that the official 7.0 release is just around the corner. But today we won’t talk about 7.0, we’ll just talk about Babel 6. Babel was in version 6 when I knew it and started using it, not in the era of 5.

In the era of Babel5, Babel was a family bucket type, and all the tools associated with Babel were installed as soon as Babel was installed.

But in babel6, the following changes are made:

  • Remove the Babel bucket installation and split it into separate modules, such as Babel-core, Babel-cli, Babel-Node, babel-Polyfill, etc.

You can see what modules Babel currently has in its Github repository.

  • Added the.babelrc configuration file, which basically all Babel translations will read.
  • Add plugin configuration, everything is pluginized, whatever code needs to be translated can be freely configured in the plugin;
  • Add preset configuration, Babel5 will translate ES6 and JSX syntax by default, babel6 translation syntax is configured in perset, PRESET is simply a use of a series of plugin packages.

Babel module introduction

Babel 6 separates the entire Babel bucket into many different modules, and it is only by knowing how these modules work that Babel can be better understood.

Some of the sample code below has been uploaded to Github, welcome to visit, welcome to Star.

Installation mode:

#Install using NPM
npm install babel-core babel-cli babel-node

#Installation Using YARN
yarn add babel-core babel-cli babel-node
Copy the code

1.babel-core

As the name implies, Babel-core is the core of Babel. Babel’s core apis are all in this module, such as Transform.

Here are a few apis in Babel-Core

  • Babel. transform: Used to transcode a string to get an AST
/* * @param {string} code Code string to be translated * @param {object} Options Optional configuration item * @return {object} */babel.transform(code: string, options? :Object)
    
// Return an object with three main parts:
{
    generated code, / / generated code
    sources map, / / the source map
    AST  // Abstract syntax tree
}
Copy the code

See here for more on AST.

Some packaging or build tools that use the Babel plugin use this method. Here is some source code imported into the Babel plugin:

//gulp-babel
const babel = require('babel-core');
/* some codes... * /
module.exports = function (opts) {
    opts = opts || {};
	return through.obj(function (file, enc, cb) {
        try {
            const fileOpts = Object.assign({}, opts, {
            	filename: file.path,
            	filenameRelative: file.relative,
            	sourceMap: Boolean(file.sourceMap),
            	sourceFileName: file.relative,
            	sourceMapTarget: file.relative
            });
            const res = babel.transform(file.contents.toString(), fileOpts);
            if(res ! = =null) {
            	//some codes}}catch (err) {
            //some codes}}}//babel-loader
var babel = require("babel-core");
/* some codes... * /
var transpile = function transpile(source, options) {
    //some code
    try {
        result = babel.transform(source, options);
    } catch (error) {
        //some codes
    }
    //some codes
}

//rollup-pugin-babel
import { buildExternalHelpers, transform } from 'babel-core';
/* some codes... * /
export default function babel ( options ) {
    //some codes
    return {
        // some methods
        transform ( code, id ) {
            const transformed = transform( code, localOpts );
            //some codes
            return {
            	code: transformed.code,
            	map: transformed.map }; }}}Copy the code

The above is some source code when some packaging tools introduce the Babel plug-in. You can see that basically the code is transcoded by calling the transform method first.

  • babel.transformFile
// Asynchronous file transcoding, callback result and transform returned by the same object.
babel.transformFile("filename.js", options, function (err, result) {
  result; // => { code, map, ast }
});
Copy the code
  • babel.transformFileSync
// Synchronizes file transcoding to return the same result as the object returned by the transform.
babel.transformFileSync(filename, options) // => { code, map, ast }
Copy the code
  • babel.transformFromAst
// Translate the AST
const { code, map, ast } = babel.transformFromAst(ast, code, options);
Copy the code

2,babel-cli

Babel-cli is a tool for code escape of JS files from the command line.

Usage:

  • Output the translated code directly on the command line
    babel script.js
    Copy the code
  • Specifying an output file
    Babel script.js --out file build.js or Babel script.js -o build.jsCopy the code

Let’s write a code with an arrow function:

//script.js
const array = [1.2.3].map((item, index) = > item * 2);
Copy the code

Then I run Babel script.js on the command line, and the output appears to be untranslated.

Since we didn’t tell Babel which types to translate, let’s see how to specify the arrow function in the translation code.

babel --plugins transform-es2015-arrow-functions script.js
Copy the code

Or add a.babelrc file to the directory as follows:

{
    "plugins": [
        "transform-es2015-arrow-functions"]}Copy the code

. Babelrc is the global configuration file of Babel. All Babel operations (including Babel-core and Babel-Node) will read this configuration.

3, Babel – node

Babel-node is installed with Babel-CLI. Once babel-CLI is installed, Babel-Node is delivered with babel-CLI. Entering babel-node on the command line starts a REPL (read-eval-print-loop), which is a JS execution environment that supports ES6.

Most ES6 syntax is supported as long as the node version is larger than 6, and the current version of Node is 8.7.0.

Babel-node can also be used to execute JS scripts directly, which is similar to using node commands directly, except that Babel is translated during execution. In addition, it is not recommended to use Babel directly in production environment, because the code generated by real-time compilation of Babel will be cached in memory, resulting in high memory usage. So let’s just get to know it.

babel-node script.js
Copy the code

4,babel-register

Babel-register is a registry for Babel that overwrites node’s require method. After babel-register is introduced, modules requiring.es6,.es,.jsx, and.js will be translated by Babel.

Here’s another experiment with the arrow function:

//test.js
const name = 'shenfq';
module.exports = () = > {
    const json = {name};
    return json;
};
//main.js
require('babel-register');
var test = require('./test.js');  // The es6 syntax in test.js will be translated into ES5

console.log(test.toString()); // See if the console output has been translated by the toString method
Copy the code

By default, babel-register ignores translation of modules in node_modules, which can be configured as follows if enabled.

require("babel-register") ({ignore: false
});
Copy the code

Babel-register and Babel-core will be installed at the same time, and there will be a register. Js file in babel-core, so there are two ways to introduce babel-Register:

require('babel-core/register');
require('babel-register');
Copy the code

However, the first method is not officially recommended because Babel-Register is already a separate module, as noted in the register. Js file of Babel-core.

TODO: eventually deprecate this console.trace(“use the babel-register package instead of babel-core/register“);

5,babel-polyfill

Polyfill a table with an object that has one side lower than the other. Polyfill a table with an object that has one side lower than the other. The main purpose of a polyfill in your code is to use existing syntax and apis to implement apis that browsers don’t yet implement, and to fix some of the browser’s flaws. For example, Array has added an includes method, and I want to use it, but it is not available on the earlier version of the browser, so I have to do compatibility:

if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(searchElement, fromIndex) {
      if (this= =null) {
        throw new TypeError('"this" is null or not defined');
      }
      var o = Object(this);
      var len = o.length >>> 0;
      if (len === 0) {
        return false;
      }
      var n = fromIndex | 0;
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
      while (k < len) {
        if (o[k] === searchElement) {
          return true;
        }
        k++;
      }
      return false; }}); }Copy the code

The above simple provides an includes method polyfill, code from MDN.

Now that you understand what polyfills mean, let’s talk about why polyfills exist in Babel. Because Babel’s translations are syntax-level translations, such as arrow functions, destructible assignments, and classes, and can’t be translated for new apis and global functions such as: Promise, it’s time to introduce Babel-Polyfill into your code to make it perfectly ES6+ friendly. The babel-Node introduced earlier will automatically introduce the Babel-Polyfill package into the code.

Introduction method:

// Do require or import at the top of the code

require("babel-polyfill");

import "babel-polyfill";

// If you use WebPack, you can also import it in the file entry array
module.exports = {
  entry: ["babel-polyfill"."./app/js"]};Copy the code

But many times we won’t use all the ES6+ syntax, and adding all the shims globally will definitely increase our code. We’ll look at other ways to add shims later.

Babel configuration

Now that you’ve covered some of the modules commonly used in Babel, let’s take a look at the Babel configuration file.babelrc.

The suffix rc comes from Linux.bashrc. Rc is short for run command, which means that the program will call this file when it is executed.

All Babel operations will read the configuration file, except for those that set options in the callback function. If the configuration file is not available, it will read the configuration from the Babel property of the package.json file.

plugins

Let’s take a quick look at plugins, the plugins in Babel that tell Babel what our code needs to be translated by configuring different plugins.

Here is a list of all the plugins currently supported by Babel.

Here’s an example:

{
    "plugins": [
        "transform-es2015-arrow-functions".// Translate the arrow function
        "transform-es2015-classes".// Translate the class syntax
        "transform-es2015-spread".// Translate the array destructor
        "transform-es2015-for-of" / / translation for -]}// If you want to add a configuration item for a plugin, write it as follows:
{
    "plugins": [// Change to an array with the second element as the configuration item
        ["transform-es2015-arrow-functions", { "spec": true}}]]Copy the code

All of this is syngram-level translation. As mentioned earlier, there are some API-level things that need to be introduced into polyfills, and Babel also has a series of plugins to support this.

{
    "plugins": [// If we use the object. assign method in our code, we use the following plug-in
        "transform-object-assign"]}// Write a code that uses object.assign as follows:
const people = Object.assign({}, {
    name: 'shenfq'
});
// After Babel translation:
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; }}}return target; };

const people = _extends({}, {
    name: 'shenfq'
});
Copy the code

The polyfill added by the transform will only be introduced into the current module, which would be a disaster if there were multiple modules using the same API and each module introduced the same Polyfill. It is also cumbersome to introduce a polyfill transform, and there is no guarantee that the manually introduced transform will be correct. A solution will be provided later: transform-Runtime.

In addition to adding Polyfill, Babel also has a tool kit that you can use directly if you have babel-cli installed:

./node_modules/.bin/babel-external-helpers > helpers.js
Copy the code

This tool kit is similar to Babel’s utils module, which is used in many different ways just like the utils in our project. For example, Babel implements Object. Assign using the _extend method in helpers. To prevent multiple references to Babel helper functions in the same file, the external-helpers plug-in can be used to pull helper functions out of files at the top to avoid multiple references.

// install: CNPM install --save-dev babel-plugin-external-helpers

/ / configuration
{
  "plugins": ["external-helpers"]}Copy the code

While this plug-in can prevent multiple references to helper functions in a file, it doesn’t directly prevent multiple references within multiple files, which is the same problem as adding a polyfill with a transform. These references are only at the Module level, and in today’s packaging toolbox, Consider how to reduce code redundancy caused by multiple modules repeatedly referencing the same code.

Of course, you can also use the packaging tool to pull out the common module in the js file that you created before you imported it directly at the top of every js file that you need to use helpers.

require('helpers');
Copy the code

After Babel helpers came the final plugin in the plugin system: Transform-Runtime. This plugin was mentioned earlier in transform-Polyfill, but it was placed after Helpers because it automatically introduced polyfill and helpers to the project.

cnpm install -D babel-plugin-transform-runtime babel-runtime
Copy the code

The transform-Runtime plugin relies on Babel-Runtime, so it is best to install Babel-Runtime as well as transform-Runtime to prevent unnecessary errors. Babel-runtime consists of three parts:

  1. core-js

    Core-js is extremely powerful, and most of the shim for ES5, 6, and 7 is implemented through ES3. Zloirock is a programmer from the Fighting clans. I heard that he is still looking for a job.

  2. regenerator

    Regenerator comes from a library at Facebook that implements Generator functions.

  3. helpers

    Yes, this is the same thing that we used earlier with babel-external-helpers

The babel-Runtime package.json file also shows what runtime depends on.

To use helpers after installing Babel-Runtime, use the following methods:

require('babel-runtime/helpers');
Copy the code

There are also some configuration items when using Runtime:

{
    "plugins": [["transform-runtime", {
            "helpers": false.// Automatic helpers
            "polyfill": false.// Automatic introduction of polyfill (core-js provided polyfill)
            "regenerator": true.// Automatically introduce the regenerator}}]]Copy the code

Compare the difference between transform-Runtime and Babel-Polyfill’s introduction of gaskets:

  1. With Runtime, you can import the polyfill as needed. You don’t need to manually configure the plugins one by one. However, the introduced polyfill is not global and has some limitations. Also, the polyfill introduced by Runtime does not override some instance methods, such as those on the Object and Array prototype chains, as mentioned earlierArray.protype.includes.
  2. Babel-polyfill solves those runtime problems. Its spacers are global and versatile. Basically all the polyfills used in ES6 are babel-Polyfill, which provides a complete ES6+ environment. Babel officials suggest that as long as the size of the Babel-polyfill is ignored, it is best to introduce it globally because it is the safest approach.
  3. The general advice is to use Babel-Runtime that doesn’t pollute the global scope when developing frameworks or libraries, and to use babel-Polyfill globally when developing Web applications to avoid unnecessary errors. And the global introduction of Babel-polyfill for large Web applications may also reduce the size of your packaged files (compared to introducing duplicate polyfills for each module).

presets

Obviously such a configuration plugin would be a hassle, but to make things easier, Babel provides a configuration item called persets.

Presets are a collection of plugins, just like retouching. Save some of the parameters from the last retouching as a preset and use it directly next time.

To translate ES6 syntax, simply configure it as follows:

// Install ES6 related preset: CNPM install -d babel-PRESET - ES2015
{
    "presets": ["es2015"]}// If you want to translate not only ES6, but also the grammar of the various proposal stages, you can do this as follows.
// Preset required for installation: CNPM install -d babel-preset-stage-0 babel-preset-stage-1 babel-preset-stage-2 babel-preset-stage-3
{
    "presets": [
        "es2015"."stage-0"."stage-1"."stage-2"."stage-3"]},// Babel also translates JSX syntax directly by introducing react presets
//cnpm install -D babel-preset-react
{
    "presets": [
        "es2015"."react"]}Copy the code

However, all of the above preset have been officially deprecated, and the only official recommended preset is babel-preset-env.

This preset has the flexibility to decide which plug-ins and polyfills to load, but the developer has to configure them manually.

// cnpm install -D babel-preset -env
{
    "presets": [["env", {
            "targets": { // Specify which environment to translate to
                // Browser environment
                "browsers": ["last 2 versions"."safari >= 7"]./ / the node environment
                "node": "6.10".//"current" uses the current version of node
                
            },
             // Whether to translate ES6's modularized syntax into other types
             Umd / / parameters: "amd" | "" |" systemjs "|" commonjs | "false, the default for 'commonjs'
            "modules": 'commonjs'.// Whether to perform the debug operation, the console will print the log of all plug-ins, the version of the plug-in
            "debug": false.// Enable certain modules forcibly, default is []
            "include": ["transform-es2015-arrow-functions"].// Disable certain modules, default is []
            "exclude": ["transform-es2015-for-of"].// Whether to automatically introduce polyfill. To enable this option, you must ensure that babel-polyfill is installed
            // Parameter: Boolean, default is false.
            "useBuiltIns": false}}]]Copy the code

There are two things to note about the last argument, useBuiltIns:

  1. If useBuiltIns is true, babel-polyfill must be introduced in the project.
  2. Babel-polyfill can only be introduced once; multiple introductions will cause global scope conflicts.

The same code, but the.babelrc configuration has useBuiltIns enabled and the.babelrc configuration has no useBuiltIns enabled. The size difference between the two js files is 70K.

file The size of the
useBuiltIns.js 189kb
notUseBuiltIns.js 259kb

reference

  1. Will ECMAScript 6 go the way of ECMAScript 4?
  2. Babel manual
  3. Babel’s official website
  4. babel-preset-env: a preset that configures Babel for you

Moreover

There is also an artifact for polyfill called polyfill. IO, which is introduced in the browser

The CDN. Polyfill. IO/v2 / polyfill…

The server returns a polyfill file based on the browser’s UserAgent, which is, amazingly, the most elegant solution to a large polyfill yet.


It took about a week to write this and a lot of research (both on Babel’s website and On Github several times) to finally get it out.

The original link