troubled

As a tool developer, the Babel correlation problem is hard to get around.

At babel@6, one of the most common feedback you get is that regeneratorRuntime is not defined

On babel@7, one of the most common responses I get is Cannot find module ‘core-js/library/fn/**’.

What are the problems that cause these problems? I think there is an issue that represents this kind of developer in particular. Don’t laugh, we have this problem in some of our internal base modules

Github.com/raisezhang/…

To sum up, Babel builds everyone’s code and inserts some module dependencies that are needed in preset or pluginpkg.dependenciesOne of the things that would otherwise be a problem is not loading the specific file or loading the wrong version of the file.

What is the fundamental reason: actually everyone right

  • @babel/preset-env
  • @babel/plugin-transform-runtime
  • @babel/runtime
  • core-js
  • @babel/polyfills
  • babel-polyfills

Familiar but unfamiliar reasons.

Today, I would like to share with you my experience of using babel@7. If there is any mistake, please point it out in time.

Preliminary knowledge

Before we get started, it’s worth taking a look at what each bag actually does

@babel/preset-env

Babel has officially scrapped the Babel Stage, ES2015, ES2016, etc., and is replaced with @babel/preset-env.

@babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!

As described in the official documentation, preset-env mainly does the conversion of JavaScript’s latest Syntax (const let… Etc.), and as an optional preset-env can also be used to convert JavaScript’s latest apis (such as array’s latest methods filter, includes, Promise, etc.).

The careful students here may have noticed my intentional emphasis on Syntax and API. Yes, there is an implied relationship between Babel’s implementation of compilation or our combination of using each preset or plugin, and there is also some historical background here, in order not to burden everyone, We just need to scratch the surface and dig deep if we need to know.

Speaking of which, I need to give you some tips:

  • In the babel@6 era, we used stages, which really only translated Syntax, and the API was handed over to babel-plugin-transform-Runtime or babel-polyfill. (This is why you can see babel-Polyfill introduced in older projects)
  • In the babel@7 years, we scrapped Stage and used preset-env, which could also provide polyfill

To sum up, I think friends will have a few puzzles

  • How does preset-env reduce pack size
  • Why is @babel/plugin-transform-runtime necessary if preset-env polyfill is available?
  • Do I need @babel/polyfill again with preset-env Polyfill

To explain these issues, it’s important to know about three key parameters of preset-env

targets:

Describes the environments you support/target for your project.

In short, this parameter determines the environment to which our project needs to adapt, such as the browser version to which we can declare adaptation, so that Babel will automatically introduce the required polyfills based on browser support.

useBuiltIns:

“usage” | “entry” | false, defaults to false

This option configures how @babel/preset-env handles polyfills.

This parameter determines how preset-env will handle polyfills.

False: Instead of polyfills, you manually import ‘@babel/polyfill’ in the import file;

However, this method was abandoned after @[email protected], and instead import the following code from the entry file

import 'core-js/stable';
import 'regenerator-runtime/runtime';
// your code

Copy the code

Not recommendedfalseThis will fill all polyfills and make the package bulky

usage:

We do not need to import the corresponding Polyfills libraries in the project entry file. Babel will inject the associated polyfills itself, based on targets, based on user code usage.

entry:

We import the corresponding Polyfills related libraries in the project entry file, for example

import 'core-js/stable';
import 'regenerator-runtime/runtime';
// your code

Copy the code

At this point Babel will import all polyfills needed into your import file based on the current targets description (all, regardless of whether you use advanced apis or not)

corejs:

String or {version: String, proposals: Boolean}, defaults to “2.0”.

corejs

Note that corejs is not a special concept, but rather governs the browser’s polyfill.

For example

const one = Symbol('one');
Copy the code

==Babel==>

"use strict";

require("core-js/modules/es.symbol.js");

require("core-js/modules/es.symbol.description.js");

require("core-js/modules/es.object.to-string.js");

var one = Symbol('one');
Copy the code

For those of you who don’t know the difference between 2 and 3, check out the official document core-js@3, Babel and a Look into the future

Corejs-2 will no longer be maintained. All polyfills for new browser features will be maintained on Corejs-3.

To summarize: With Corejs-3, enableproposals: trueSo we can use the apis of the proposals phase.

conclusion

Polyfills injected with preset-env pollute globally, but are manageable for their own application.

So it is recommended that business projects use.babelrc this way

{
  "presets": [["@babel/preset-env",
      {
        "targets": {
          "chrome": "58" // Fill in as you need
        },
        "useBuiltIns": "entry"."corejs": {
          "version": 3."proposals": true}}]],"plugins": []}Copy the code
import 'core-js/stable';
import 'regenerator-runtime/runtime';
// Entry file code
Copy the code

The reason for this configuration is: targets sets the minimum environment configuration that our business project needs to support, useBuiltIns is set to Entry, and all polyfills that the minimum environment does not support are imported into the entry file (even if you do not use them in your business code). This is a way to balance the final package volume and stability, why say stability, because it is difficult to ensure that the tripartite package cited has dealt with the problems of polyfill. UseBuiltIns can also be set to Usage if you are sure that polyfill handles your three-way dependencies properly.

For the general public projects, the above configuration (leaving aside personalized) should be enough, but the pursuit of extreme students will have two problems:

Problem 1: There is still a certain degree of code duplication, for example:

import a from 'a';

export default a;
Copy the code

==Babel==>

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _a = _interopRequireDefault(require("a"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var _default = _a.default;
exports.default = _default;
Copy the code

The _interopRequireDefault method, of course, can be made into a separate module, so that the packaging size can be changed to a smaller size (even less is more desirable).

Problem 2: For the project, polyfill will pollute the global environment, which is acceptable, but as a Library, I hope it will not pollute the global environment

Both good questions, so here’s where @babel/ plugin-transform-Runtime comes in.

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime

That’s the official description

A plugin that enables the re-use of Babel’s injected helper code to save on codesize.

The obvious reason for this plug-in is to reuse the correlation code injected by Babel.

What does @babel/plugin-transform-runtime do? What does @babel/plugin-transform-runtime do?

The transform-runtime transformer plugin does three things:

Automatically requires @babel/runtime/regenerator when you use generators/async functions (toggleable with the regenerator option).

Can use core-js for helpers if necessary instead of assuming it will be polyfilled by the user (toggleable with the corejs option) Automatically removes the inline Babel helpers and uses the module @babel/runtime/helpers instead (toggleable with the helpers option).

Here’s an example:

import a from 'a';

export default a;
Copy the code
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports."__esModule", {
  value: true
});
exports.default = void 0;

var _a = _interopRequireDefault(require("a"));

var _default = _a.default;
exports.default = _default;
Copy the code

Does this solve the problem mentioned above?

As for question 2, the global contamination of polyfill is not going to be expanded, because it involves source code explanation. All you need to know is that polyfill implemented by @babel/ plugin-transform-Runtime does not affect the global contamination. Therefore, it is more suitable for Library authors

Also, one must wonder what would happen if @babel/plugin-transform-runtime turned on corejs and @babel/preset-env turned on useBuiltIns. The used advanced API polyfill will use the Runtime global pollution-free scheme (note: @babel/ env targets setting will fade), and the unused API polyfill will use the preset global pollution-free scheme.

So to sum up:

For component developers, ignore targets of @babel/preset-env and enable corejs of @babel/ plugin-transform-Runtime. If this is for the business developer, please enable targets for @babel/preset-env, And turn off corejs for @babel/ plugin-transform-Runtime

Tell the important questions three times

corejs option Install command
false npm install –save @babel/runtime
2 npm install –save @babel/runtime-corejs2
3 npm install –save @babel/runtime-corejs3

According to the above option, @babel/ Runtime should be used as the dependencies of the project

According to the above option, @babel/ Runtime should be used as the dependencies of the project

According to the above option, @babel/ Runtime should be used as the dependencies of the project

conclusion

  • If you are a business project developer:@babel/plugin-transform-runtime, recommended closurecorejs, the introduction of polyfill by@babel/preset-envDone, that is, onuseBuiltIns(If other configurations are required, configure them according to the requirements.)
{
  "presets": [["@babel/preset-env",
      {
        "targets": {
          "chrome": 58
        },
        "useBuiltIns": "entry"."corejs": {
          "version": 3."proposals": true}}]],"plugins": [["@babel/plugin-transform-runtime",
      {
        "corejs": false}}]]Copy the code

And import the following contents in the entry file

import 'core-js/stable';
import 'regenerator-runtime/runtime';
// Entry file code
Copy the code
  • If you’re a Library developer:@babel/plugin-transform-runtime, it is recommended to enablecorejsBy polyfill@babel/plugin-transform-runtimeIntroduction.@babel/preset-envShut downuseBuiltIns.
{
  "presets": [["@babel/preset-env",]],"plugins": [["@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3."proposals": true}}]]}Copy the code

But if you are careful, you must have found a new problem

The new problem

why@babel/preset-envDo not use non-polluting global polyfills (note that non-polluting global polyfills must be created by@babel/plugin-transform-runtimeThe introduction of);

Why is it necessary to use polyfill that doesn’t pollute the world@babel/plugin-transform-runtimeAnd at the same time I had to compromisepreset-env targets(Please note that the default is to introduce polyfill from The Runtime, since it does not pollute globally)

How to solve it?

I’m sorry that in the formal Babel system there was no way to solve this problem, but Babel was aware of it and babel-polyfills.

Note babel-polyfills not @babel/polyfills, we’ll move to the end of the article.

@babel/runtime

You don’t need to delve into it, but look at it in the context of @babel/ plugin-transform-Runtime.

core-js

corejs

It doesn’t need to be delved into, just look at it in the context of @babel/preset-env.

@babel/polyfills

Deprecated at [email protected], please use @babel/preset-env.

babel-polyfills

babel-polyfills

The motivation for this library is the question we raised at the end of the @babel/ plugin-transform-Runtime section:

Motivation

  • It wasn’t possible to use @babel/preset-env’s targets option with “pure” ponyfills, because @babel/plugin-transform-runtime is a completely separate package.
  • We forced our users to use core-js if they wanted a Babel integration. core-js is a good and comprehensive polyfill, but it doesn’t fit the needs of all of our users.

However, this library is currently in the experimental stage. According to my understanding of Babel, it is not recommended to introduce it into production at present. We can keep an open mind and keep an eye on it.

As for those who want to try something new, the authorities have also given them an upgrade.

(I’m an avid author. O. O.)

Great summary

There is no TLDR for this article; Whether you’re a tool developer, Library developer, or business developer, have a little patience and read through this article because 99% of developers I’ve observed don’t understand it.


“Ant RichLab front-end team” is committed to sharing high-quality technical articles with you, welcome to pay attention to our Zhihu/Nuggets column, share the article with your friends, and grow together 🙂

Our team is looking for front-end engineers and interns for class 22: front-end/full stack development, interactive graphics technology, low code/engineering, front-end architecture, data algorithms, etc. Team technical atmosphere is good, room for rise is big, resume can directly hit me [email protected]