The introduction of

This question is for myself, but I believe there are many students like me. The use of Babel has been confined to webpack and browser development environments for the last six months. So many Babel bags, I don’t even know what they do. Babel-register, babel-Runtime, the difference between presets, the difference between transform Runtime and Babel-polyfill, helpers. Although Babel’s tutorials are plentiful online, it still takes some effort to answer your own questions. So take time to summarize. If you are already familiar with the above concepts, you do not need to read further.

The example code is all on Github, and each folder has a detailed README explaining how I use it. Take a look at the use cases and clone them yourself.

Version changes

To be honest, it’s been Babel 6 since I started working on the front end, but that doesn’t stop me from seeing the big changes. The previous version of Babel 5 was a whole family bucket, including various packages and plugins, as much as possible in a single installation. If you install NPM install Babel, you will get a warning. Babel 6 was released on October 30, 2015 with the following updates:

  • Broken down into core packages,babel-core.babel-node.babel-cli.
  • Without the default conversion, you now need to manually add the plugin. That is, plug-in
  • Preset is added.
  • Added.babelrc file to facilitate custom configuration.

Almost. I don’t think I need to know anything else.

package

There are a lot of packages in Babel, so it’s important to figure out what they do in order to use the tool well.

babel-core

You can view it as a compiler for Babel. The core apis of Babel are all in there, like Transform, which deals with transcoding. It abstracts our JS code into an AST, which stands for Abstract Syntax Tree, a tree representation of the abstract syntactic structure of the source code. It defines a tree structure for parsing JS syntax. In other words, the new syntax of ES6 is different from the old syntax, so how do we define this syntax? Therefore, it is necessary to first transform into AST to discover the kind of this grammar and do corresponding processing respectively to convert it into ES5.

The main API:

var babel = require('babel-core');
var transform = babel.transform;
Copy the code
  • babel.transform(code: string, options? : Object)
transform("code", options) // => { code, map, ast }

Copy the code
  • babel.transformFile(filename: string, options? : Object, callback: Function)
var path = require('path');
babel.transformFile(path.resolve(__dirname) + "/test.js", {
  presets: ['env'].plugins: ['transform-runtime'],},function(err, result) {// { code, map, ast }
    console.log(result);
});
Copy the code
  • babel.transformFileSync(filename: string, options? : Object)
var result = babel.transformFileSync(path.resolve(__dirname) + "/test.js", {
  presets: ['env'],
  plugins: ['transform-runtime']}); console.log(result,'res');

Copy the code
  • babel.transformFromAst(ast: Object, code? : string, options? : Object)

Reverse, you pass in the AST, parse it into code code.

options

babel-cli

Provides a command line to run Babel. You can transcode the file by Babel filename. installed

npm install --save-dev babel-cli

npm isntall babel-cli -g
Copy the code

Using the corresponding is

node_module/.bin/babel script.js --out-file script-compiled.js

babel script.js --out-file script-compiled.js
Copy the code

Specific use or see the official document, I will not move the document.

babel-external-helpers

Babel – A command in the CLI that generates a piece of code containing all of Babel’s helper functions.

First we need to know what helpers are. Babel has many helper functions, such as toArray and JSX conversion functions. These are functions that we use for Babel Transform, that we put in this package called Babel-helpers. If babe detects a file that needs these helpers when compiling, it will put them at the top of the module when compiling. Like this,

(function(module, exports, __webpack_require__) {

function _asyncToGenerator(fn) { return function () {}; }// Define the helper at the top of the module

// some code 
// Async syntax is transformed by transform-async-to-generator, wrapped with helper functions that consume generator.
const func = (() = > {
  var _ref = _asyncToGenerator(function* () {
    console.log('begin');
    yield new Promise(function (resolve) {
      setTimeout(function () {
        resolve();
      }, 1000);
    });
    console.log('done'); }); })})Copy the code

However, if multiple files need to be provided, these helpers will be repeatedly referenced, resulting in the definition of a copy for each module, resulting in code redundancy. So Babel provides this command to generate a JS file containing all helpers for direct reference. And then through a plugin, to detect the global existence of this module, there is no need to redefine.

Use:

  1. Run babel-external-helpers to generate helpers.js
 node_modules/.bin/babel-external-helpers > helpers.js
Copy the code

Note: The packages for the sample code are loaded into the project, that is, local. Also, you can install the global installation directly. 2. Install the plugin

npm install --save-dev babel-plugin-external-helpers
Copy the code
  1. Then add it to the Babel configuration file
{
  "plugins": ["external-helpers"]}Copy the code
  1. Import helpers.js file
require('./helpers.js');
Copy the code

That’s fine, and it still saves a lot of code. In addition, if you use transform-Runtime, you don’t need to generate helpers.js, which will be discussed later in the babel-Runtime section.

babel-node

Babel-cli is a command that implements Node’s ability to execute scripts and write code on the command line. Just pick up two chestnuts.

Execute the script

The Node environment definitely does not support JSX

// test.js
const React = require('react');
const elements = [1, 2, 3].map((item) => {
  return (
    <div>{item}</div>
  )
});

console.log(elements);
Copy the code

Test.js: test.js: test.js: test.js: test.js: test.js

Nodetest.js // ErrorCopy the code

But using babel-node will do.

 node_modules/.bin/babel-node --presets react test.js
Copy the code

Presets react are arguments equivalent to

{
  "presets": ["react"]}Copy the code

The execution is normal.

Node command line write code

Note: All code examples in this article are executed under Node version 4.8.4.

Write a destruct assignment, run node directly, not supported.

Run node_modules/.bin/babel-node –presets env

You get a value of 1.

Babel-node is a node that can be used to write scripts. So it is not suitable for production environments. In addition, Babel-Node already has polyfill built in and relies on babel-Register to compile scripts. Ok, so what’s a Babel-Register

babel-register

npm install babel-register --save-dev
Copy the code

Babel-node compiles code from it, and as you can see, it’s really just a compiler. We can also introduce it in our code require(‘babel-register’) and execute our code through Node.

It works by overwriting node’s own require, adding hooks, and then triggering Babel compilation while requiring other modules. The file code that you introduce require(‘babel-register’) will not compile. Only other code introduced through require will. Babel-node means writing a temporary file in memory, importing babel-register at the top, and then importing our script or code.

For example, JSX is still executed in Node and compiled via Babel. JSX code a.js can be compiled and output to a B.js, and then node B.js can be executed. But it’s too cumbersome for development. Let’s see how register works:

// register.js imports babel-register and configures it. It then introduces the entry file to execute the code
require('babel-register') ({presets: ['react']});require('./test')
Copy the code
// test.js this file is JSX...
const React = require('react');
const elements = [1.2.3].map((item) = > {
  return (
    <div>{item}</div>)});console.log(elements);
Copy the code
// execute $node register.jsCopy the code

It is characterized by real-time compilation, without the need for output files, to compile at the time of execution. So it’s good for development. In summary, node is used to run applications and do real-time compilation, usually in conjunction with other plug-ins as compilers, such as mocha tests.

Babel-register (‘babel-core/register’) can also be used to require(‘babel-core/register’), as required (‘babel-register’). However, the Babel team has made register independent and it will be abolished from Babel-Core someday (7.0 liters), so we’d better use Babel-Register for now. babel-core/register.js

babel-runtime

npm install babel-runtime --save
Copy the code

This package simply references core-js and ReGenerator, and the production environment compiles them into the dist directory, maps them, and uses them. So what are core-JS and Regenerator? The first thing we need to know is that babel-core transforms syntax, but it doesn’t support build-ints (Eg: Promise, Set, Map), prototype function (Eg: Promise, Set, Map), and prototype function (Eg: Promise, Set, Map). Array.reduce, String.trim), class static function (Eg: Array.form, Object.assgin), regenerator (Eg: Generator, async), etc. That’s why core-js and Regenerator are used.

core-js

Core-js is a modular standardized library for JavaScript, which contains ES5 (LLDB: Freeze), ES6 promises, symbols, collections, iterators, typed Arrays, ES7 + proposals, etc. That is, it contains almost all of the gaskets of JavaScript’s latest standards. But why doesn’t it implement generator as well… 😁

For example, only a single reference is required
require('core-js/array/reduce');
require('core-js/object/values');
Copy the code

regenerator

It’s a library from Facebook, links. Generator /yeild, async/await.

So babel-Runtime is a pure implementation of core-js and regenerator import and export, for example here is the filter function definition, does a relay and handles esModule compatibility.

module.exports = { "default": require("core-js/library/fn/array/filter"), __esModule: true };
Copy the code

helpers

Remember when we talked about helpers at Babel-external-Helpers, helpers. Helpers in Babel-Runtime are the same as helpers.js generated by babel-external-helpers. It just puts each helper in a separate folder. This will be referenced directly from babel-Runtime when the transform-Runtime helper needs to be used.

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
Copy the code

File structure:

use

Require (‘babel-runtime/core-js/object/values’);

But these modules are made esModule compatible processing, is introduced in the above module is {” default “: the require (” core – the js/library/fn/array/filter”), __esModule: True}, you must add.default. So what we’re looking for is a plugin that automates this, and that’s what babel-plugin-transform-Runtime does. Let’s put this in plugin.

babel-polyfill

npm install babel-polyfill --save
Copy the code

Babel-runtime is already a bunch of polyfills, why there is a similar package here, which also references core-JS and Regenerator, and the shim support is the same. According to the official website, Babel-Polyfill is designed to simulate a complete ES2015 + environment and is intended to be used by applications rather than libraries/tools. And when you use babel-node, the polyfill is automatically loaded (we talked about this at the end of babel-Node).

In other words, it simulates the execution environment of our application with perfect ES6 + support, since both browser and Node environments support ES6 + differently. It is overloaded global variables (um participant: Promise), and a static method on the prototype and the class (um participant: Array. Prototype. Reduce/Array. The form), so as to achieve support for es6 +. Unlike Babel-Runtime, babel-Polyfill is introduced into your project once, like the React package, and compiled into production with the project code.

use

Let’s use it with babel-Register

// index.js
require('babel-core/register') ({});require('babel-polyfill'); // Yes, you need to import manually.
require('./async');
Copy the code
// async.js
async function a() {
  console.log('begin');
  await new Promise((resolve) = > {
    setTimeout((a)= > {
      resolve();
    }, 1000)})console.log('done');
}
a();
Copy the code
$ node index.js
Copy the code

It works perfectly. Note: Babel-polyfill is only used to inject shims into the current environment globally, ES6 syntax (LLDB: arrow func, esModules) still needs to add plugins to transform.

plugins

Plugins include the Babel compilation process. Babel compilation is divided into three steps:

  1. Parser: Parses the Babylon to AST.
  2. Transform [s] : All the plugins/presets, further do syntax and other custom translation, still AST.
  3. Generator: Finally, babel-generator generates an output string.

So plugins reinforce translation in the second step, so if we were to write our own plugin, it would be to iterate over the AST structure.

babel-plugin-transform-runtime

As we have seen above, transform-Runtime is designed to facilitate the use of babel-Runtime. It analyzes our AST to see if there are any references to spacers from Babel-Rumtime (via mapping), and if so, inserts the required spacers at the top of the current module. Try it on:

npm install babel-plugin-transform-runtime
Copy the code
/ / before compilation
console.log(Object.values({ 1: 2 }));

Copy the code
node_modules/.bin/babel --plugins transform-runtime values.js
Copy the code
/ / the compiled
'use strict';

var _values = require('babel-runtime/core-js/object/values');

var _values2 = _interopRequireDefault(_values);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

onsole.log((0, _values2.default)({ 1: 2 }));
Copy the code

In addition, it has several configurations

/ / the default value
{
  "plugins": [["transform-runtime", {
      "helpers": true."polyfill": true."regenerator": true."moduleName": "babel-runtime"}}]]Copy the code

If you only need regenerator and do not need polyfill in core-js then you can set polyfill to false in options. Helpers is set to false, which means that babel-plugin-external-helpers is disabled. For example, if async is translated, asyncToGenerator is used, and helpers are redefined for each file. ModuleName, that’s the library to use, and you can change babel-Runtime to something like that.

The transform – runtime contrast Babel – polyfill

In fact, through the above introduction we already know what they are doing, here is a little summary of the distinction. I refer to both babel-Runtime and babel-plugin-transform-Runtime as transform-Runtime because they are better used together.

  • Babel-polyfill is a shim for the current environment to inject these ES6 + standards with the benefit of reference once, no longer worrying about compatibility, and it is a global package that can be used anywhere in the code. The disadvantage is also obvious, it may pollute some of the native methods and rewrite the native methods. If the current project already has a polyfill package, you can only keep one of them. And introducing such a package all at once adds a lot of volume. If you’re just using a few features, there’s no need. If you’re developing a larger application and you use new features frequently and consider compatibility, go ahead and introduce them.
  • Transform-runtime is a new feature that uses plugin to automatically recognize and replace code. You don’t need to introduce it, just install babel-Runtime and plugin. The advantage is that you replace on demand, you detect which polyfill you need, you introduce which polyfill, if you only use part of it, the packaged file will be much smaller than babel-Polyfill. And transform-Runtime does not contaminate native objects, methods, or affect other polyfills. Therefore, transform-Runtime is more suitable for development kits and libraries. On the one hand, the size is small enough. On the other hand, users (developers) will not pollute the global native methods and cause side effects by referencing our tools and packages. The downside is that as the application grows, the same Polyfill does the same work (detection, replacement) for each module, even though polyfill is just a reference, which is not efficient enough to compile.

Add babel-Runtime to dependencies. It is simply a library with a collection of polyfills that are imported into the project and packaged with the project code. However, it does not introduce all of them. The plugin will require whichever you use. So even if your final project is just one of the files require(‘ babel-Runtime /core-js/object/values’), it is production-dependent for this package.

Note: Babel-Polyfill does not necessarily pollute the global environment. When importing the JS and running it, it determines whether it currently has a method or not, and then overwrites it. As shown above

presets

Plugins can be quite difficult to configure. Es6 + compiles include plugins, such as converting esModule to Commomjs in order to use esModule in Node. Use transform-ES2015-modules-commonJS, asyncToGenerator, React JSX conversions, etc.

Presets are combinations of plugins, you can also interpret them as packages… There are mainly

  • env
  • es2015
  • react
  • lastet
  • For stage-x syntax, refer to TC39

I don’t think most of the presets need to be introduced, the official website is more detailed. And babel-preset-lastet (including ES2105, ES2016, ES2017) is the same as env by default, which means that the four presets including lastest will be replaced by babel-preset-env. That is:

{ "presets": ["latest"]} = = = {"presets": ["env"]}Copy the code

babel-preset-env

This preset is really a magic tool that automatically determines plugins and polyfills you need according to the current running environment. According to the support status of each ES standard feature in different browsers and Node versions, the mapping relationship between a feature and plugins is maintained, and the required plugins are finally determined.

Preset – env configuration

Details:

{
  "presets": [["env",
      {
        "targets": { // Support the environment
          "browsers": [ / / the browser
            "last 2 versions"."safari >= 7"]."node": "current"
        },
        "modules": true.// Set ES6 module translation to commonJS by default
        "debug": true.// debug, console when compiling
        "useBuiltIns": false.// Enable automatic support for polyfill
        "include": [].// Which plugins are always enabled
        "exclude": []  // Enforce unenabled plugins to prevent certain plug-ins from being enabled}]],plugins: [
    "transform-react-jsx" // If you need to support JSX, this thing should be installed separately.]}Copy the code

Debug and useBuiltIns.

debug

After debug is enabled, targets, plugins, and polyfill information are displayed in the compilation result

Using targets:
{
  "chrome": "59"."android": "4.4.3"."edge": "14"."firefox": "54"."ie": "10"."ios": "10"."safari": "Seven"."node": "4.8.4"
}

Modules transform: commonjs

Using plugins:
  check-es2015-constants {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  transform-es2015-arrow-functions {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  transform-es2015-block-scoped-functions {"android":"4.4.3"."ie":"10"."safari":"Seven"}
  transform-es2015-block-scoping {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}... Using polyfills: es6.typed.array-buffer {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  es6.typed.int8-array {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  es6.typed.uint8-array {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  es6.typed.uint8-clamped-array {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}
  es6.typed.int16-array {"android":"4.4.3"."ie":"10"."safari":"Seven"."node":"4.8.4"}...Copy the code

useBuiltIns

Env will automatically determine what polyfill we need based on our environment, and the size of the packaged code will be much smaller, but this is all using useBuiltIns and requires you to install babel-Polyfill and import. It will enable a plug-in that replaces your import ‘babel-Polyfill’, not as a whole, but as a separate polyfill depending on your configured environment and individual needs. I tried to see if it really worked, and here’s how I compared it:

Step1: first is such a test compiled code, JSX, object. values, async. Env is configured the same as above except for useBuiltIns. It is then packaged with webpack + babel-loader to generate build.js

require('./async');
// import 'babel-polyfill';

const React = require('react');
const elements = [1.2.3].map((item) = > {
  return (
    <div>{item}</div>)});console.log(elements);

async function a() {
  console.log('begin');
  await new Promise((resolve) = > {
    setTimeout((a)= > {
      resolve();
    }, 1000)})console.log('done');
}
a();

console.log(Object.values({ 1: 2 }));

console.log(Array.isArray([]));

Copy the code

Step2: Then package, get build.js, and execute by setting different parameters. Get the table below

Under the condition of preset – env useBuiltIns: true
Without introducing polyfill Build. js code size 158K, node build.js execution error.
The introduction of polyfill The build.js volume is 369K. The bag does get smaller.
  • Preset – ES2015 and introduce polyfill plugins with a transform-Regenerator is not as convenient as Env. Volume 418K, execute pass. The problem is that you have to install a lot of plugins.

Specific process, screenshots here

The final conclusion is that using useBuiltIns does reduce the volume, much better than importing ‘babel-polyfill’ directly.

Step3: and then… I tried env again, using transform-Runtime. Without useBuiltIns, without introducing babel-polyfill. The build.js volume is 234K.

Gee, it seems to be smaller. Remember, our Babel-Polyfill is configured with an execution environment to see which polyfills you need. The Transform-Runtime, which discovers what our code needs to polyfill, is much less of course. So, back to the question of which… 😓 refer to the summary above.

then

Helpers. For a development project, use preset-env and import ‘babel-polyfill’, but helpers seem to have no place to configure. In addition, I tried two files using async function separately, and after compiling each module defined asyncToGenerat function, in this case, I think I ended up generating helpers.js file myself.

conclusion

Now it seems like the best configuration for larger projects is preset-env, determine your runtime environment, add useBuiltIns if necessary, and generate a helpers.js file. However, it all depends on your needs. My premise is to develop bigger “projects”, but knowing these things, you will make your own choices.

Babel’s configuration

Babel is currently officially recommended to be written to.babelrc files. You can also add Babel fields to package.json. Without a configuration file, you can pass the configuration as a parameter to babel-CLI

  • .babelrc
{
  "presets": [
    "env"]."plugins": [["transform-runtime", {
      "helpers": true."polyfill": true."regenerator": true."moduleName": "babel-runtime"}}]]Copy the code
  • Wrote package. Json
"babel": {
  "presets": [
    "env"],}Copy the code
  • babel cli
babel script.js --plugins=transform-runtime --presets=env
Copy the code

Cooperate with other tools

webpack

In addition to Babel’s own package, install an additional Babel-loader to work with webpack. Add loader configuration to webpack.config.js

  module: {
    rules: [{test: /\.js$/.use: ['babel-loader'].exclude: /node_modules/,}}]Copy the code

mocha

The code in the project is written in ES6 +, but when doing unit testing, the testing framework does not know your esModule, some syntax of ES6 +, Mocha is a Node program, so you need to convert esModule to CommomJS or something.

Mocha supports compilers, which are specified by –compilers, so we use Babel, for example

// summing function add.js const add = (x, y) => x + y; export default add;Copy the code
// Test script add.test.js
import { expect } from 'chai'; // chai is the assertion library
import add from './add';

describe('Es6 add two numbers', () => {
  it('2 + 4 = 6', () => {
    expect(add(2.4)).equal(6); })});Copy the code
./node_modules/mocha/bin/mocha --compilers js:babel-register add.test.js
Copy the code

Because Mocha is running node programs after all and is good for real-time compilation, you can use babel-Register as a compiler.

The last

It took me two or three days to figure out what the packages were for, but I wondered if I should spend my time studying them. Tools are always there to use, and in Babel’s case, what it does with ast? However, when I see my own output, I think it is necessary. In addition, because I have a further understanding of the tools, I can better use them in the project, rather than copying them all at once. The code can be used normally, but there may be a lot of unnecessary things, resulting in the size of the code. “How can you cut a chicken with an ox knife?” I think there is a need for the spirit of excellence. I hope it helps.

My personal blog address, https://github.com/sunyongjian/blog, welcome to subscribe to, star, thank you.