Dissect Babel — Take one small step forward for the architect
When talking about Babel’s role, many people’s first reaction is: to implement the API Polyfill.
In fact, Babel is much more than that as a cornerstone of front-end engineering.
As a large family, there are many concepts in the Babel ecosystem, such as: Preset, Plugin, Runtime, etc.
These concepts make Babel intimidating for beginners, and the babel-Loader configuration for WebPack is the only way to understand it.
This article will begin with the core functions of Babel, demystifying the Babel family step by step, and taking a small step forward as an architect.
What is the Babel
Babel is a JavaScript compiler.
As a JS compiler, Babel receives the input JS code, goes through the internal processing process, and finally outputs the modified JS code.
Within Babel, the following steps are performed:
-
Parsing Input Code into an AST (abstract syntax tree) is called parsing
-
Editing the AST, which is called the transforming step
-
Printing the edited AST as an Output Code is called printing
From the source code of the Babel repository, it can be found that Babel is a Monorepo composed of dozens of projects.
Babel-core provides the capability for the three steps mentioned above.
Inside Babel-Core, to be more specific:
-
Babel-parser implements the first step
-
Babel-generator implements the third step
To understand the second step, we need to take a brief look at AST.
The structure of the AST
Go to AST Explorer, select @babel/ Parser as the parser, and type on the left:
const name = ['ka'.'song'];
Copy the code
The AST can be parsed as a tree structure in JSON format:
Inside Babel-Core:
-
Babel-traverse traverses the AST tree in depth-first mode
-
For each path traversed, babel-types provides node type data for modifying AST nodes
Therefore, the overall Babel underlying compilation capability consists of the following components:
Now that we know the underlying capabilities of Babel, let’s look at what the upper layers can do based on those capabilities.
Babel’s superior abilities
Based on Babel’s ability to compile JS code, the most common upper level capabilities of Babel are:
-
polyfill
-
DSL transformations (such as parsing JSX)
-
Syntax transformations (such as parsing high-level syntax into currently available implementations)
Due to space constraints, this section only covers polyfill and syntactic conversions.
polyfill
As a front end, the most common Babel ecology libraries are probably @babel/ Polyfill and @babel/preset-env.
Degraded implementation of advanced syntax and polyfill of apis can be implemented using @babel/polyfill or @babel/ Preset -env.
Babel is just a compiler for JS.
The answer is: core-js
The core – js profile
Core-js is a set of modular JS standard library, including:
-
All the way to polyfill ES2021
-
Promise, Symbols, iterators and other features of the implementation
-
Feature implementation in ES proposal
-
Cross-platform WHATWG/W3C features such as urls
Core-js is also a Monorepo consisting of multiple libraries, including:
-
core-js-builder
-
core-js-bundle
-
core-js-compat
-
core-js-pure
-
core-js
We introduce a few of them:
core-js
Core-js provides the core implementation of Polyfill.
import 'core-js/features/array/from';
import 'core-js/features/array/flat';
import 'core-js/features/set';
import 'core-js/features/promise';
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)); / / = > 32
Copy the code
Using core-js directly pollutes the global namespace and object prototype.
For example, in the previous example, the Array prototype was modified to support the flat method of Array instances.
core-js-pure
Core-js-pure provides a separate namespace:
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)); / / = > 32
Copy the code
This use does not pollute the global namespace and object prototype.
core-js-compat
According to Browserslist, core-js-compat maintains a set of features that need to be supported in different host environments and versions.
Browserslist provides support for ES features in different browsers and Node versions
Such as:
"browserslist": [
"not IE 11"."maintained node versions"
]
Copy the code
Represents: non-IE11 versions and all versions maintained by the Node.js Foundation.
@babel/polyfill and core-JS
@babel/polyfill can be thought of as core-js plus regenerator-Runtime.
Regenerator-runtime is a runtime dependency of generator and async/await
Using @babel/polyfill alone will import core-JS in full, resulting in too much project packaging.
As of Babel V7.4.0, @babel/polyfill is deprecated and can be directly referenced to core-js and Regenerator-Runtime instead
To solve the problem of excessive packing volume caused by the full introduction of Core-js, we need to use @babel/ PRESET -env.
The meaning of the preset
Before introducing @babel/preset-env, let’s know what preset means.
Initially, Babel has no additional capabilities and its workflow can be described as follows:
const babel = code= > code;
Copy the code
The plugin provides the ability to intervene in babel-core externally, and the Webpack-like plugin provides the ability to intervene in the WebPack compilation process externally.
Plugins fall into several categories:
-
@babel/plugin-syntax-* syntax related plugin for new syntax support. For example, babel-plugin-syntax-decorators provides syntax support for decorators
-
@babel/plugin-proposal-* for ES proposal feature support, such as babel-plugin-proposal-optional-chaining is optional chain operator feature support
-
@babel/plugin-transform-* Is used to transform code, and the corresponding syntax plug-in is used inside the transform plug-in
A set consisting of multiple plugins is called preset.
@babel/preset-env
Using @babel/ Preset -env, features in Core-JS can be packaged as needed, which can significantly reduce the final package size.
Here on demand, divided into two granularity:
-
Granularity of the host environment. Package all the features needed for that environment according to the different hosting environment
-
Granularity by use. Package only the features that are used
Let’s look at them in turn.
Granularity of the host environment
When we configure the browserslist file in the project directory (or in the targets property of @babel/preset-env, or in the browserslist property of package.json) with the following parameters:
not IE 11
maintained node versions
Copy the code
Features required for all versions of Node maintained by the Node.js Foundation that are not IE11 will be bundled into the final package.
Obviously this is taking advantage of the core-js capability that we just introduced, core-js-compat under Monorepo.
Granularity by use
It would be better to pack only the features we used.
Set @babel/ Preset -env’s useBuiltIns attribute to Usage.
Such as:
A. s:
var a = new Promise(a);Copy the code
B.j s:
var b = new Map(a);Copy the code
When the host environment does not support Promise and Map, the output file is:
A. s:
import "core-js/modules/es.promise";
var a = new Promise(a);Copy the code
B.j s:
import "core-js/modules/es.map";
var b = new Map(a);Copy the code
When the host environment supports these two features, the output file is:
A. s:
var a = new Promise(a);Copy the code
B.j s:
var b = new Map(a);Copy the code
Further optimize the packaging volume
Open Babel Playground and type:
class App {}
Copy the code
You will find that the compiled result is:
function _classCallCheck(instance, Constructor) { if(! (instanceinstanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); }}var App = function App() {
"use strict";
_classCallCheck(this, App);
};
Copy the code
_classCallCheck is the auxiliary method.
If multiple files use the class feature, then each file package will contain _classCallCheck in the corresponding Module.
To reduce packaging volume, it is better to refer to all modules that need helper methods from the same place rather than maintain a copy yourself.
@babel/ Runtime contains all of the Babel helper methods as well as the regenerator-Runtime.
Simply introducing @babel/ Runtime doesn’t work because Babel doesn’t know when to refer to helper methods in @babel/ Runtime.
Therefore, @babel/ plugin-transform-Runtime is also needed.
This plugin will change all use of helper methods at compile time from maintaining a copy to importing them from @babel/ Runtime.
So we need to set @babel/plugin-transform-runtime to devDependence because it is used at compile time.
I’ll set @babel/runtime to dependence because it’s used at runtime.
conclusion
This article takes a bottom-up look at the members of the Babel family that the front-end day-to-day business development deals with. They include:
The underlying
@babel/core (consisting of @babel/parser, @babel/traverse, @babel/types, @babel/generator, etc.)
They provide Babel with the ability to compile JS.
Note: @babel/core is the repository name, and babel-core is the repository name
In the middle
@babel/plugin-*
Babel exposes apis that allow developers to interfere with its ability to compile JS
The upper
@babel/preset-*
A collection of plug-ins that will be used for daily development.
For students who aspire to become front-end architects, Babel is the cornerstone of front-end engineering, so it is necessary to understand and use it.
It’s not easy to see here. Give yourself a big hand.