This article is based on Babel 7.4.5.
The main Babel modules are shown in the figure above and are described separately next.
1. @babel/core
@babel/core is primarily a method of converting source code to code compatible with the target environment based on configuration.
import * as babel from "@babel/core"; babel.transform("code();" , options, function(err, result) { result.code; result.map; result.ast; });Copy the code
2. @babel/cli
Babel /cli is a command line tool provided by Babel for compiling source code on the command line.
First install dependencies:
npm install --save-dev @babel/core @babel/cliCopy the code
Create a new js file:
Let array = [6]; array.includes(function(item){ return item>2; }) class Robot { constructor (msg) { this.message = msg } say () { alertMe(this.message) } } Object.assign({},{ a:1,b:2 }) const fn = () => 1; new Promise();Copy the code
Perform conversion:
npx babel index.js --out-file out.jsCopy the code
You can see that the output code has not changed because there is no configuration to determine how to do the transformation.
3. @babel/plugin*
Babel is converted by plug-ins, such as plugin-transform-arrow-functions for arrow functions.
First install the plug-in:
npm install --save-dev @babel/plugin-transform-arrow-functionsCopy the code
Plugins can be used via @babel/cli parameters or configuration files:
-
@babel/cli
npx babel index.js --out-file out.js --plugins=@babel/plugin-transform-arrow-functionsCopy the code
You get the out.js file and see that the arrow function has been converted.
let array = [1, 2, 3, 4, 5, 6]; array.includes(function (item) { return item > 2; }); class Robot { constructor (msg) { this.message = msg } say () { alertMe(this.message) } } Object.assign({}, { a: 1, b: 2}); const fn = function () { return 1; }; new Promise();Copy the code
-
The configuration file babel.config.js(javascript) or. Babelrc (json) is more commonly used.
module.exports = function (api) { api.cache(true); const plugins = [ "@babel/plugin-transform-arrow-functions" ]; return { plugins }; }Copy the code
4. @babel/presets
We use a variety of ES6 syntax in index.js. Importing plug-ins one by one is a hassle. Presets are a set of predefined plug-ins. Some presets are assembled for common environments (you can also configure them yourself) :
- @babel/preset-env
- @babel/preset-flow
- @babel/preset-react
- @babel/preset-typescript
Let’s use @babel/preset-env as an example (NPM install @babel/preset-env) :
module.exports = function (api) {
api.cache(true);
const presets = [
["@babel/preset-env"]
];
return {
presets
};
}Copy the code
The result is as follows. You can see that the arrow function is compiled, the ES6 class, and the let declaration are compiled.
"use strict"; 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; } var array = [1, 2, 3, 4, 5, 6]; array.includes(function (item) { return item > 2; }); var Robot = /*#__PURE__*/ function () { function Robot(msg) { _classCallCheck(this, Robot); this.message = msg; } _createClass(Robot, [{ key: "say", value: function say() { alertMe(this.message); } }]); return Robot; } (); Object.assign({}, { a: 1, b: 2 }); var fn = function fn() { return 1; }; new Promise();Copy the code
But you can see that the instance methods of the array include, the static methods of the object, and promise are not compiled.
This is because Babel calls Javascript syntax syntax syntax and API syntax that we can override with functions like includes, map, includes, Promise, Anything we can think of rewriting can be attributed to the API. Syntax means things like arrow functions, let, const, class, dependency injection Decorators, etc., that we can’t override in Javascript, imagine that in unsupported browsers you can’t use the let keyword anyway.
@babel/presets only convert syntax by default, we need to use @babel/polyfill to provide support for API.
5. @babel/polyfill
@babel/ Polyfill consists of core-js2, a Facebook open source library that supports generator and Async functions, and Regenerator-Runtime, a JS standard library that contains implementations of different versions of javascipt syntax.
Simply introduce @babel/ Polyfill at the top of the entry to the JS file and you can use the ES6 API freely in the following code.
But the whole @babel/polyfill package is so bulky that we usually only use part of it, and introducing the whole library is obviously inappropriate. So you can introduce only the methods used:
import 'core-js/features/array/from'; // <- at the top of your entry point import 'core-js/features/array/flat'; // <- at the top of your entry point import 'core-js/features/set'; // <- at the top of your entry point import 'core-js/features/promise'; // <- at the top of your entry point Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] [1, [2, 3], [4, [5]]].flat(2); // => [1, 2, 3, 4, 5] Promise.resolve(32).then(x => console.log(x)); / / = > 32Copy the code
If you don’t want to pollute the global namespace (for example, keep it isolated when writing an NPM library). A clean version can be introduced:
import from from 'core-js-pure/features/array/from'; import flat from 'core-js-pure/features/array/flat'; import Set from 'core-js-pure/features/set'; import Promise from 'core-js-pure/features/promise'; from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] flat([1, [2, 3], [4, [5]]], 2); // => [1, 2, 3, 4, 5] Promise.resolve(32).then(x => console.log(x)); / / = > 32Copy the code
The useBuiltIns attribute in the configuration item of Preset -env can facilitate the use of @babel/ Polyfill.
-
useBuiltIns:false(default)
: Not at this timepolyfill
Do the operation. If the introduction of@babel/polyfill
, ignore configured browser compatibility and import allpolyfill
. -
useBuiltIns:"entry"
: Introduces incompatibilities based on the configured browser compatibilitypolyfill
. It needs to be added manually in the entry fileimport '@babel/polyfill'
, will automatically based onbrowserslist
Replace it with anything the browser isn’t compatible withpolyfill
. -
useBuiltIns:"usage"
: There is no need to manually introduce @babel/polyfill at the top of the file, it will be added as needed according to the use in the code.
Using useBuiltIns:”usage” as an example here, the babel.config.js file looks like this:
module.exports = function (api) {
api.cache(true);
const presets = [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"targets":{
"browsers":["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
];
return {
presets,
// plugins
};
}Copy the code
The resulting compilation results:
"use strict"; require("core-js/modules/es6.promise"); require("core-js/modules/es6.object.to-string"); require("core-js/modules/es6.object.assign"); require("core-js/modules/es7.array.includes"); 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; } var array = [1, 2, 3, 4, 5, 6]; array.includes(function (item) { return item > 2; }); var Robot = /*#__PURE__*/ function () { function Robot(msg) { _classCallCheck(this, Robot); this.message = msg; } _createClass(Robot, [{ key: "say", value: function say() { alertMe(this.message); } }]); return Robot; } (); Object.assign({}, { a: 1, b: 2 }); var fn = function fn() { return 1; }; new Promise();Copy the code
You can see the introduction of polyfill on demand. However, core-js2 is used by default when the core-JS version is not specified in the configuration file. The following information is displayed:
WARNING: We noticed you’re using the
useBuiltIns
option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the
corejs
option.
This is because core-js3 has been released, @babel/polyfill does not support smooth transition from core-js2 to core-js3, so in Babel 7.4, @babel/ Polyfill is deprecated (core-js2 only). Instead, core-js3 and Regenerator-Runtime are introduced directly.
import "@babel/polyfill";
// migration
import "core-js/stable";
import "regenerator-runtime/runtime";
Copy the code
There are many advantages to using Core-js3. First, it is new and contains many new features, and second, it works with @babel/ Runtime (more on that later). For more, see core-js@3, Babel and a Look into the Future.
Using core-js3 is babel.config.js as follows:
module.exports = function (api) {
api.cache(true);
const presets = [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs":3,
"targets":{
"browsers":["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
];
return {
presets,
// plugins
};
}
Copy the code
A closer look at the compilation results above reveals two problems.
- Many helper functions (such as _classCallCheck) are introduced when higher-order syntax is converted to lower-order syntax. When there are many files, introducing these helper functions for each file will increase the file size. How can these helper functions be separated into separate modules and then introduced as needed?
- Although polyfill is introduced on demand, it pollutes the global namespace and may conflict with the user’s local methods when you write a common library. For example, if you introduce promises in polyfill into your library, the users themselves define their own promises, which can lead to conflicts. How do you isolate the Polyfill API introduced in your public library?
To solve these two problems, we need to use @babel/ Runtime and @babel/plugin-transform-runtime.
6. @babel/runtime
@babel/runtime depends on @babel/helpers and regenerator-Runtime helpers, so helper functions can be imported from that, so manual helpers are impossible, So Babel provides @babel/ plugin-transform-Runtime to do these transformations for us.
The babel.config.js file is:
module.exports = function (api) {
api.cache(true);
const presets = [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"targets":{
"browsers":["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
];
const plugins = [
["@babel/plugin-transform-runtime"]
]
return {
presets,
plugins
};
}Copy the code
The resulting compilation looks like this:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); require("core-js/modules/es6.promise"); require("core-js/modules/es6.object.to-string"); require("core-js/modules/es6.object.assign"); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); require("core-js/modules/es7.array.includes"); var array = [1, 2, 3, 4, 5, 6]; array.includes(function (item) { return item > 2; }); var Robot = /*#__PURE__*/ function () { function Robot(msg) { (0, _classCallCheck2.default)(this, Robot); this.message = msg; } (0, _createClass2.default)(Robot, [{ key: "say", value: function say() { alertMe(this.message); } }]); return Robot; } (); Object.assign({}, { a: 1, b: 2 }); var fn = function fn() { return 1; }; new Promise();Copy the code
We can see that our first problem has been satisfactorily solved.
The second problem is solved using the corejs parameter in @babel/ plugin-transform-Runtime option. The default is false and polyfill is not handled. You can set it to different versions of Core-JS.
For example, if core-js2 is used, install it first
npm install --save @babel/runtime-corejs2Copy the code
The configuration file is:
module.exports = function (api) {
api.cache(true);
const presets = [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"targets":{
"browsers":["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
];
const plugins = [
["@babel/plugin-transform-runtime",{corejs:2}]
]
return {
presets,
plugins
};
}
Copy the code
The result is:
"use strict"; var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault"); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass")); require("core-js/modules/es7.array.includes"); var array = [1, 2, 3, 4, 5, 6]; array.includes(function (item) { return item > 2; }); var Robot = /*#__PURE__*/ function () { function Robot(msg) { (0, _classCallCheck2.default)(this, Robot); this.message = msg; } (0, _createClass2.default)(Robot, [{ key: "say", value: function say() { alertMe(this.message); } }]); return Robot; } (); (0, _assign.default)({}, { a: 1, b: 2 }); var fn = function fn() { return 1; }; new _promise.default();Copy the code
You can see that polyfill is introduced with an alias to avoid global variable contamination, but you can see that the instance method includes is not handled accordingly. This was a problem core-JS2 didn’t solve, and it was perfectly fixed with the release of Core-JS3 in March 2019. We set corejs to 3 and get the following result:
"use strict"; var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault"); var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); var _assign = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/assign")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass")); var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes")); var array = [1, 2, 3, 4, 5, 6]; (0, _includes.default)(array).call(array, function (item) { return item > 2; }); var Robot = /*#__PURE__*/ function () { function Robot(msg) { (0, _classCallCheck2.default)(this, Robot); this.message = msg; } (0, _createClass2.default)(Robot, [{ key: "say", value: function say() { alertMe(this.message); } }]); return Robot; } (); (0, _assign.default)({}, { a: 1, b: 2 }); var fn = function fn() { return 1; }; new _promise.default();Copy the code
7. @babel/register
After compiling with Babel, our source code is different from the code running under production.
Babel-register provides dynamic compilation. In other words, our source code can actually run in production without Babel compilation.
Let’s start by installing babel-Register under the project:
$ npm install --save-dev @babel/registerCopy the code
Then require in the entry file:
require('@babel/register')
require('./app')Copy the code
By introducing @babel/ Register in the header of the entry file, we can use any ES2015 feature in our app file.
The downside, of course, is dynamic compilation, resulting in a loss in speed and performance. (This can be used when launching the test script)
7. @babel/node
We said that babel-Register provides dynamic compilation that allows our source code to actually run in production – but we still need to make some adjustments, such as adding an entry file and requiring (‘@babel/register’) in that file. Babel-node can actually do a line of source code without changing it:
$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.jsCopy the code
However, don’t use Babel-Node in a production environment, because it is dynamically compiled source code and applications start up very slowly
reference
Babel.docschina.org/docs/en/bab…
Github.com/zloirock/co…
blog.hhking.cn/2019/0…
segmentfault.com/a/11…
zhuanlan.zhihu.com/p/…
blog.zfanw.com/babel-…
www.thebasement.be/up…