Setting up the Test Environment
In the root directory NPM init-y, generate the basic package.json, create SRC, and create the index.js file in SRC for balel to compile later. Then create a. Babelrc file in the root directory as the configuration file for Babel.
Install dependencies
Install some dependency modules of Babel according to the process of the official website
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
Copy the code
Configuration. Babelrc
Babel compilation automatically reads configuration from the.babelrc file in the directory
{
"presets": [
[
"@babel/env",
{
"targets": {
"chrome": "43"
},
"useBuiltIns": "usage",
"corejs": {
"version": 2
}
}
]
],
"plugins": []
}
Copy the code
There may be two preset fields in the configuration file. This is a brief introduction:
-
Plugins: used to convert code from high version syntax to a collection of low version plug-ins such as @babel/plugin-transform-arrow-functions to convert arrow functions to ES5.
-
Preset: Since many Bable plug-ins may be used in a project, it would be inconvenient to add them one by one each time. To solve this problem, Babel provides a combination of plug-ins, known as perset.
Test es6 syntax
let a = 1;
const foo = () = > {
console.log("Arrow function")}Copy the code
Run the following command
// compile SRC files and print them to lib
npx babel src --out-dir lib
// Compile the result
"use strict";
var a = 1;
var foo = function foo() {
console.log("Arrow function");
};
Copy the code
You can see that it has been packaged into es5 syntax.
Test the higher version API
Includes is a new API of ES7. What will it be packaged into during the test?
let arr = [1.2.3];
console.log(arr.includes(1))
Copy the code
After the package
"use strict";
require("core-js/modules/es7.array.includes");
var arr = [1.2.3];
console.log(arr.includes(1));
Copy the code
This file overwrites the includes method in Array and implements the new includes method in ES7 using the lower version syntax.
babel-polyfill
In fact, the syntax above is not transformed by Babel, but by babel-polyfill, which is worth talking about. Put the official website first.
A quick explanation: Babel only escapes JS syntax by default, but does not convert some new APIS, such as include, array. from, etc. What Babel-Polyfill does is help you accommodate these higher version syntaxes.
Composition: Babel-polyfill contains two packages core-js and Regenerator-Runtime.
Core-js: This library is used to convert some of the higher-version syntax to lower-version syntax.
Regenerator-runtime: library that provides transformations to async await.
But strangely 🤔, we didn’t introduce Babel-Polyfill anywhere in the project, how did it work 😳😳. This is actually thanks to the @babel/env default that we configured in.babelrc, so what exactly does that do?
@babel/env
Env is the one we use most often in Babel, and the core purpose of env is to learn the characteristics of the target environment through configuration and then make only the necessary transformations. For example, THE target browser supports ES2015, es2015 preset is not needed, so the code can be smaller (converted code is always longer) and the build time can be shorter.
If no configuration items are written, env is equivalent to Latest and is equivalent to the sum of ES2015 + ES2016 + ES2017 (excluding stage-x plug-ins). The list of plug-ins included with env is maintained here.
So let’s see now, why didn’t we introduce polyfill and it just went into effect? It is no surprise that the env default is introduced. We can look at useBuiltIns in Babel /env
"usage"
|"entry"
|false
, defaults tofalse
.This option configures how
@babel/preset-env
handles polyfills.When either the
usage
orentry
options are used,@babel-preset-env
will add direct references tocore-js
modules as bare imports (or requires). This meanscore-js
will be resolved relative to the file itself and needs to be accessible.
When useBuiltIns = entry, polyfill needs to be referenced at the top of the code
/ / input
import "@babel/polyfill"
let arr = [1.2.3];
console.log(arr.includes(1))
// Compile the result
"use strict"; .require("core-js/modules/es6.array.copy-within.js");
require("core-js/modules/es6.array.fill.js");
require("core-js/modules/es7.array.includes.js"); .var arr = [1.2.3];
console.log(arr.includes(1));
Copy the code
As you can see, in this mode Babel brings in all the content not supported by Chrome 43, resulting in a very large package size, and we only need an includes method here.
When useBuiltIns = Usage, it will help us load on demand and do not need to be imported manually
/ / input
let arr = [1.2.3];
console.log(arr.includes(1))
// Compile the result
"use strict";
require("core-js/modules/es7.array.includes.js");
var arr = [1.2.3];
console.log(arr.includes(1));
Copy the code
If array.includs is called, then reference it. But is that really the case?
/ / input
const Foo = function () {};
Foo.prototype.includes = function () {};new Foo().includes();
// Compile the result
"use strict";
require("core-js/modules/es7.array.includes.js");
var Foo = function Foo() {};
Foo.prototype.includes = function () {};
new Foo().includes();
Copy the code
As you can see, I didn’t call array.includes, but it was introduced anyway, so it should use the method name to determine if it needs to be introduced.
When useBuiltIns = false, the API is not processed.
disadvantages
But there are two drawbacks to the above two approaches,
-
They polyfill by directly modifying methods on the prototype object of the global constructor, which can cause global contamination and may cause unexpected bugs.
-
When translating, Babel sometimes uses auxiliary functions to help, such as:
/ / input class Test {} // Compile the result "use strict"; function _classCallCheck(instance, Constructor) { if(! (instanceinstanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); }}var Test = function Test() { _classCallCheck(this, Test); }; Copy the code
In class syntax, Babel defines the _classCallCheck function to help. If a project has multiple files and each file has a class, the final package of the project will have 100 _classCallCheck functions. This is obviously unreasonable.
To avoid both, we can use the @babel/ plugin-transform-Runtime plugin.
@babel/plugin-transform-runtime
The plugin relies on the @babel/ Runtime-corejs2 or @babel/ Runtime-corejs3 packages, which you can think of as implementations of polyfill
npm install @babel/runtime-corejs2 --save
# or
npm install @babel/runtime-corejs3 --save
Copy the code
Assume the following code:
class Test {}
const set = new Set(a);console.log([].includes)
Copy the code
. Babelrc configuration
{
"presets": [["@babel/env",
{
"targets": {
"chrome": "43"
},
"useBuiltIns": "usage"."corejs": {
"version": 2}}]],"plugins": []}Copy the code
Packing results:
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));
class Test {}
const set = new _set.default();
console.log([].includes);
Copy the code
Core-js2 only handles Set, but not includes, because core-js2 handles class/static methods used in code, not methods in the prototype chain.
Now change corejs of. Babelrc to 3 and compile
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _set = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set"));
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
var Test = function Test() {(0, _classCallCheck2.default)(this, Test);
};
var set = new _set.default();
console.log((0, _includes.default)([]));
Copy the code
Core-js3 handles Set and includes, so you can see that core-js3 also handles methods on the prototype chain.
From the above, we can see that the plugin-transform-Runtime plugin solves the above two problems
- Instead of directly modifying methods on the object prototype object, it is imported from a unified module, avoiding contamination of global variables.
- Auxiliary functions are introduced from a unified module to avoid the existence of multiple auxiliary function codes in the code.