Hello, Babel

Writing in the front

In fact, learning Babel is a plan of my Q3 2019, because a project I did at that time needed to match Babel by myself, I also encountered some difficulties, and found that I did not know much about Babel, so I decided to have a good look at Babel. However, after I solved the problem, the Babel learning didn’t come to an end due to the demands of the business and my laziness (which was the final reason)… Drag to now 0.0

It has already entered the year of 2020, and I have not had time to summarize my 19 years, but IN 2020, I will set a flag that should have been done long ago. In this year’s Q1, I will thoroughly understand Babel, not to say thoroughly, at least I will make sure that I can directly solve all the Babel problems of the team.

I’ve been reading some blogs about Babel lately, and I’ve come to realize that Babel is actually very interesting, and that studying something will improve my technical growth. I’m done, Ollie!

Basic understanding of Babel

In fact, I did some homework before LEARNING Babel, and I have some basic understanding of Babel, so LET me talk about my basic understanding

What’s a Babel?

Babel is a converter that converts higher-order ES2015+ versions of code into code that browsers can parse (backward compatibility).

Why Babel?

I do front-end work, mainly using JS. Js versions are updated year by year, with more convenient and useful methods. Up to now, ECMAScript has reached ES10 and has been iterated for 10 versions. However, the mainstream JS language supported by most browsers is ES2014 syntax, there are many ES2015 and ES2015+ syntax is not supported, that is, you use these syntax part of the browser is not supported, will report an error.

But advanced grammar is convenient. I just want to use it. What should I do? It’s easy to see why you should use Babel, which compiles ES2015+ syntax into a common language that browsers can understand. Here’s a simple example:

ES2014 function (n) {return n + 1}Copy the code

How does Babel work?

After reading some blogs about Babel, I realized that Babel is a collection of plugins, one after another.

Babel parses the code into an AST(abstract syntax tree), then modifies the AST with a corresponding plug-in, and outputs the modified AST.Copy the code

Some other features of Babel

By default, Babel only escapes JS syntax and does not convert new apis, such as global API objects like Promise, Generator, Symbol, etc. Babel does not compile.

In this case, Babel's Babel-Polyfill or babel-Runtime library is used in conjunction with the babel-plugins-Transform-Runtime plug-in to add polyfills on demand.Copy the code

By making a list of my own brain maps of Babel learning, I will begin my Babel learning journey.

Babel version changes – Simple to understand

Babel version changes

Most of the projects that use Babel now use Babel7.x, but it is important to know about the major changes in each version of Babel. This article will briefly document the major changes that I have seen in several versions of Babel

babel5.x

Babel5 is basically a family bucket, a vue-CLI kind of thing, complete with packages and add-ons. Babel5 basically lets you run it through a single installation, Get everything you want as much as possible.

babel6.x

The main reason why Babel5 was upgraded to Babel6 was that NPM installed everything at once, but some things were not needed, resulting in a large package size, so babel6 removed some packages and plug-ins and could be installed independently, my guess is like this…

Changes to Babel6:

  • Split the one-time installation into several core packages: babel-core, babel-cli, babel-polyfill, and so on…
  • Plug-in, according to their own needs to manually add plugin, this I think can, everything is about a load on demand;
  • You can configure Babel using the.babelrc file for custom configuration. Is babel5 configurable only in package.json? If you know, please comment on 0.0
  • Added preset, which I’ll talk about in detail when I learn. I hear it’s awesome.
  • Babel5 can just convert require to import, babel6 can’t, so if you use require you have to add.default, but I think you can just change it to import, because I had a dream about it, My solution was to introduce a plugin called @babel/ plugin-transform-modules-commonJS to compile require as import.
  • Babel6, babel6, Babel6, Babel6

babel7.x

Here we go, babel7 is the best

  • NPM scope package @balbel/ XXX is used to install packages. There will be no conflicts with other packages.
  • State-x presets for all stages have been abandoned and replaced by plugin. There is also an upgrade plan on the official website. You can install the babel-upgrade package, which will automatically replace the stage-x you used before with the corresponding plugin
Copy
https://github.com/babel/babel/blob/master/packages/babel-preset-stage-0/README.md
Copy the code
  • More powerful env
  • We used to have plugins like es2015 (etc.) like @babel/plugin-transform-es2015-classes. Now we can just write @babel/plugin-transform-classes
  • Some packages are deleted, such as babel-core/register.js, and @babel/register should be used directly
  • That’s about it

Babel configuration usage resolution

By default, Babel only escapes JS syntax and does not convert new apis, such as global API objects such as Promises, generators, and symbols. After I’ve learned how to configure babe L, it’s just a matter of watching me compile these new apis into it.
This article is based on Babel7.8.0. I mainly record some important module packages needed to configure Babel, step by step to a configuration of Babel (babel.config.js configuration as an example).
Some of the Babel packages covered in this article are:@babel/core.@babel/cli.@babel/plugin*.@babel/preset-env.@babel/polyfill.@babel/runtime.@babel/plugin-transform-runtime.
So, without further ado, let’s go?

@babel/core

@babel/core is a package that mainly contains some core methods to convert code. I won’t introduce some API methods in detail. To quote from the official website: “All conversions will use local configuration files”, so our babel.config.js configuration file will be important later; So let’s put it together without further ado. Gogogo

npm install --save-dev @babel/core
Copy the code

@babel/cli

@babel/cli is the built-in CLI for Babel, which allows us to compile our files from the command line.

npm install --save-dev @babel/cli
Copy the code

Once installed, you can compile your files like this:

npx babel study.js --watch --out-file study-compiled.js
Copy the code

–out-file is used to output the compiled target file to the corresponding file. If you want to compile every time you change the target file, add the –watch option; There are a few other options, of course, but these are enough for me as I learn about Babel and configuration.

If you change the Babel configuration and find that the compiled file is not compiled in real time, please note that if you change the configuration file, you need to run this command again. Otherwise, Babel cannot read the new configuration.

If you have already created the study.js file and executed this command, you will see that the corresponding study-compile.js has not changed because we haven’t started writing the Babel configuration file yet, so don’t panic, just start.

@ Babel/plugin * and @ Babel/preset – env

The Babel plugin and Babel presets are the two main modules in the Babel configuration, so I’ll put them together.

@babel/plugin*

Babel is based on Plugins, so if you don’t have any Plugins in your configuration file, the input and output are the same. Plugins, Plugins, Plugins, Plugins, Plugins. Let me compile a simple arrow function to see how the Babel plugin works. Here we go, we need the configuration file. All the following configuration is in the babel.config.js file

/* babel.config.js */

module.exports = {
  presets: [
  ],
  plugins: [
    "@babel/plugin-transform-arrow-functions"
  ]
}
Copy the code

Then execute our previous CLI command to get something like this:

/* study.js */
const study = () => {}

/* study-compiled.js */
const study = function () {};
Copy the code

Of course, if we want to use es6’s new exponentiation operator, we just need to add @babel/ plugin-transformation-exponentiation-operator:

/* babel.config.js */

module.exports = {
  presets: [
  ],
  plugins: [
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-exponentiation-operator"
  ]
}
Copy the code

The corresponding ES6 syntax becomes:

/* study.js */
const exponentiation = 2 ** 2

/* study-compiled.js */
const exponentiation = Math.pow(2, 2);
Copy the code

\

@babel/preset-env

Babel presets are the only presets that can be added to es6. Babel presets are the only presets that can be added to ES6.

Presets can be set up in advance without having to import plugins one by one. Many presets are available, such as PRESET -env (a set of plug-ins to deal with ES6 + specification grammars), preset-stage (a set of plug-ins to deal with grammars that are still in the proposal stage), Of course this preset is obsolete in Babel 7+), preset-react (a set of plugins that deal with react syntax), etc.

Let’s focus on preset-env: Preset -env is an intelligent preset that allows you to write your code in ES6 +, and it will load the needed plug-ins to make your life better… Next, let’s install @babel/preset-env without any plugin and see how it looks:

/* babel.config.js */

module.exports = {
  presets: [
    "@babel/preset-env"
  ],
  plugins: [
  ]
}
Copy the code

The corresponding ES6 syntax becomes:

/* study.js */ const study = () => {} const arr1 = [1, 2, 33] const arr2 = [...arr1] const exponentiation = 2 ** 2 // new API New Promise(() => {}) new Map() /* study-compile.js */ var study = function study() {}; var arr1 = [1, 2, 33]; var arr2 = [].concat(arr1); var exponentiation = Math.pow(2, 2); New Promise(function () {}); new Map();Copy the code

You’ll notice that the es6+ syntax is compiled, we don’t have any plugins, and you should also see that the new API methods aren’t compiled, so we’ll see what happens next when we talk about polyfill.

Browserslist integration

For preset-env, we can also provide a targets configuration item to specify the run environment, that is, we can configure the target browser environment, and Babel will compile the code that can run in the target browser environment. I believe some of you have encountered a blank screen for your project on an ios phone with a lower version. In fact, some syntax is not supported by the lower version of ios. At this time, we can directly configure the code that can be supported by the ios 7 browser environment:

/* babel.config.js */ module.exports = { presets: [ [ "@babel/preset-env", { 'targets': { 'browsers': ['ie >= 8', 'iOS 7'] // Support IE8, directly use iOS browser version 7}}]], []}Copy the code

Of course, Babel’s Browserslist integration also supports specifying the target environment in package.json or creating a new.browserslistrc file. Browserslist configuration source

@babel/ Polyfill (an integrated package consisting of core-js2 and Regenerator-Runtime)

This is because Babel, by default, only escapes JS syntax and does not convert new apis. For example, global API objects such as Promise, Generator and Symbol are not compiled by Babel. That’s when @babel/ Polyfill comes out. The usage is simple, install a wave first, then we just need to introduce @babel/ Polyfill at the top of the entry file to use the new API.

/* study.js */ import '@babel/polyfill' // new API new Promise(function () {}); /* study-compiled.js */ require("@babel/polyfill"); New Promise(function () {});Copy the code

Minor detail: Import is compiled to require, and if you want the compiled module to introduce a specification or import, you can just add “modules”: false to the preset-env configuration item. Modules of options: “amd” | “umd” | “systemjs” | “commonjs” | “CJS” | | “auto” false, the default value is “auto”

Sometimes we don’t use that many new apis in our projects, but @babel/ Polyfill will introduce all the polyfills in the browser environment, and the whole package will become very large. What should we do if we want to introduce polyfills in the target environment as needed? At this point we can use the useBuiltIns property in the preset-env configuration item to introduce polyfills as needed.

/* babel.config.js */ module.exports = { presets: [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "Entry ", 'Targets ': {' Browsers ': [' IE >= 8', 'iOS 7'] // Browsers with IE8 are supported, directly using iOS browser version 7}}]], plugins: []}Copy the code

At this point, polyfills will be introduced at the entrance only for those above IE8 and iOS 7 that do not support the API.

End result:

/* study.js */ import '@babel/polyfill' // new API new Promise(function () {}); /* study-compiled.js */ import "core-js/modules/es6.array.copy-within"; import "core-js/modules/es6.array.every"; . // omit some import "core-js/modules/web.immediate"; import "core-js/modules/web.dom.iterable"; import "regenerator-runtime/runtime"; New Promise(function () {});Copy the code

At this point, you will notice that the import ‘@babel/polyfill’ is gone and the corresponding polyfill for our target environment is introduced. Import ‘core-js/… @babel/ Polyfil is an integration package made up of core-js2 and Regenerator-Runtime.

At this point, you might wonder, if I only use the Promise API in my project, can I just introduce the Promise API? The answer is yes! , let’s take a good look at the useBuiltIns property in the preset-env configuration item.

useBuiltIns

Options: “usage” | | “entry” false, the default is false.

We have already used Entry, meaning that the entry will introduce all the unsupported apis of the target browser environment based on the browser compatibility we configured.

When usage is set to Usage, Babel will scan each of your files and check which new apis you are using, follow up our browser compatibility, only introduce polyfill for the corresponding API, we will set useBuiltIns attribute to Usage to see the compilation effect:

/* study.js */ import '@babel/polyfill' // new API new Promise(function () {}); /* study-compiled.js */ import "core-js/modules/es.object.to-string"; import "core-js/modules/es.promise"; New Promise(function () {});Copy the code

I asked if you were handsome! Totally on demand, awesome!

If the useBuiltIns option is not specified in the configuration file, core-js2 will be used by default:

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.

As mentioned earlier, @babel/ Polyfil is an integration package made up of Core-js2 and Regenerator-Runtime. Core-js3 is now available and stable. But core-JS2 was no longer maintained at 18; @babel/polyfil introduces 2 instead of 3, and @babel/polyfill is no longer recommended in Babel7.4.0. So the core-JS official now recommends that we introduce core-js and Regenerator-Runtime/Runtime directly when using polyfill instead of @babel/ Polyfil to prevent major changes.

Of course, we need to specify the core-js version in the preset-env configuration item so that there are no more warnings ⚠️ :

/* babel.config.js */ module.exports = { presets: [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "Entry" and "corejs", "3", 'the targets' : {' browsers: [' not ie > = 8', '7' iOS] / / support ie8, direct use of iOS browser version 7}}]], the plugins: []}Copy the code

@babel/helpers @babel/helpers regenerator-Runtime

In some cases, syntax conversions can be complicated, and Babel introduces some helper functions, such as converting es6 classes:

/* study.js */ class Test {} /* study-compiled.js */ function _classCallCheck(instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Test = function Test() { _classCallCheck(this, Test); };Copy the code

As you can see, helper functions have been introduced to handle class transitions. But the problem is, if there are many files that use complex syntax conversions, this is easier. Some helper functions are very complex and have a lot of code. Wouldn’t each file have to define these functions once, and each file would have a lot of code? If we could pull all these helper functions into a common package, and just introduce the corresponding functions where they are needed, the amount of compiled code would be greatly reduced. In this case, the @babel/ plugin-transform-Runtime plugin is needed to work with @babel/ Runtime. Add @babel/ plugin-transform-Runtime to the plugin option and see what it looks like:

/* study.js */
class Test {}

/* study-compiled.js */
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};
Copy the code

Of course, if we use this plug-in just to reduce the amount of code in the compiled file, it is too little, and not necessary.

Babel /plugin-transform-runtime also has one of the most important effects: for example, promises need to be polyfilled, which has the side effect of contaminating global variables. It would be nice if we were just working on a business project and no one else needed it. But if we’re maintaining a common thing, like a common component library, and we do it this way, some of your polyfills might change some of the global apis, the side effects will be obvious, and people will have problems using your component library. The @babel/ plugin-transform-Runtime plugin provides corejs, which provides a sandbox environment for polyfills so that they don’t pollute global variables and have no side effects.

Remember to install the @babel/runtime-corejs2 package (2 is fine for stable versions), and note that if not configured, the sandbox environment will not be provided. Then configure corejs in @babel/ plugin-transform-Runtime:

/* babel.config.js */ module.exports = { presets: [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "Usage ", "corejs": "3", 'targets': {' Browsers ': [" IE >= 8", "iOS 7"] // Support Internet Explorer 8, use iOS browser version 7}}]], plugins: [ [ "@babel/plugin-transform-runtime", { "corejs": 2 } ] ] }Copy the code

Let’s take a look at the compiled result:

/* study.js */
new Promise(() => {})
class Test {}

/* study-compiled.js */
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";
import _Promise from "@babel/runtime-corejs2/core-js/promise";

new _Promise(function () {});
var Test = function Test() {
  _classCallCheck(this, Test);
};
Copy the code

section

  • Make sure to restart the compile command after you modify the Babel configuration item, otherwise it will not take effect
  • Maintaining a common component library or some other common library recommends using @babel/ Runtime in conjunction with @babel/ plugin-transform-Runtime to set up sandbox environments

Reproduced: A man who aspires to be a bull