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');
var result = babel.transformFileSync(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 -gCopy the code
Using the corresponding is
node_module/.bin/babel script.js --out-file script-compiled.js
babel script.js --out-file script-compiled.jsCopy 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:
-
Run babel-external-helpers to generate helpers.js
node_modules/.bin/babel-external-helpers > helpers.jsCopy 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.
-
Install the plugin
npm install --save-dev babel-plugin-external-helpersCopy the code
-
Then add it to the Babel configuration file
{ "plugins": ["external-helpers"]}Copy the code
-
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
node testJs / / an errorCopy the code
But using babel-node will do.
node_modules/.bin/babel-node --presets react test.jsCopy 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-devCopy 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
/ / $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 --saveCopy 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 --saveCopy 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');
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.jsCopy the code
It works perfectly.
plugins
Plugins include the Babel compilation process. Babel compilation is divided into three steps:
- Parser: Parses the Babylon to AST.
- Transform [s] : All the plugins/presets, further do syntax and other custom translation, still AST.
- 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-runtimeCopy the code
/ / before compilation
console.log(Object.values({ 1: 2 }));
Copy the code
node_modules/.bin/babel --plugins transform-runtime values.jsCopy 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. Polyfill overwrites the original method. 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.
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 is obsolete and replaced by babel-preset-env.
{ "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:
The 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
- Use env, no useBuiltIns, no
require('babel-polyfill'
.
Build. js code size 158K, node build.js execution error.
- Use env, useBuiltIns, no
require('babel-polyfill'
.
Same result as above.
- Using env, without useBuiltIns,
require('babel-polyfill'
Because of the introduction of polyfill, build.js code volume instantly 420K, execution passes.
- Using env, plus
useBuiltIns: true
.require('babel-polyfill'
The build.js volume is 369K. The bag does get smaller.
- Use ES2015 and introduce Polyfill
Plugins add a transform-Regenerator, which is not as convenient as env. Volume 418K, execute pass. The problem is that you have to install a lot of plugins.
Specific experimental process, screenshots stamp here
The final conclusion is that using useBuiltIns does reduce the volume, much better than importing ‘babel-polyfill’ directly.
And then… I tried env again, using transform-Runtime.
- Env, no useBuiltIns, no 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=envCopy 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
// Sum functionadd.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.jsCopy 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 is github.com/sunyongjian… Subscribe to star. Thank you.