The public,

What is the Polyfill

A polyfill is an API or syntax that is not supported by the host environment. A polyfill is an API or syntax that is not supported by the host environment.

Example:

[1, 2, 3].fill(4); / / (4, 4, 4]

Patch:

if (! Array.prototype.fill) {Object.defineProperty(Array.prototype, 'fill', {value: function(value) {// return O; }}); }

Patching method

In general, there are three main ways to patch:

  1. Manual patching
  2. Automatic patching based on coverage
  3. Patch as needed, depending on browser characteristics

The three methods can be used independently or in combination. Let’s take a look at each of the three methods in action.

Manual patching

When it comes to patching manually, I need to read about @babel/polyfill

@babel/polyfill

Has been in
[email protected]. *Abandoned. The following
@babel/preset-envWill be mentioned

@babel/polyfill is a collection of two NPM packages, core-js and regenerator-runtime.

The latest version of core-js is 3.x.x. @babel/polyfill relies on the 2.x.x version of core-js, the core-js@2 version is missing some features. Such as Array. Prorotype. Includes. So @babel/polyfill is no longer recommended from [email protected].

However, we will explain the traditional @babel/polyfill usage in this section, which is very helpful to understand the manual patching method

Here’s how to use it:

Approach 1: Introduce Babel’s polyfill.js file directly into the HTML file

The Demo code repository address is tutor-polyfill01

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta Name ="viewport" content="width=device-width, initial-scale=1.0"> </title> </head> <body> <! Js --> <script SRC ="./lib/polyfill.js"></script> <script SRC ="./index.js"></script> </body> </ HTML >

The above example is to put the @ Babel/polyfill/dist/polyfill. Js file copy to your project, direct global introduced in HTML

Approach 2: Introduce @babel/polyfill into the project build entry

The Demo code repository address is tutor-polyfill03

Install @ Babel/polyfill

npm i @babel/polyfill

Main code section

import "@babel/polyfill"

var promise = Promise.resolve('hello babel')
console.log(promise)

HTML part

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial-scale =1.0"> <title> document< /title> </head> <body> <script SRC ="./dist/main.js"></ body> </ HTML >

Method 3: Introduce core-js/stable and regenerator-runtime/runtime into the project build entry

The Demo code repository address is tutor-polyfill04

This method requires the installation of the NPM packages core-js@3 and Regenerator-Runtime separately. This method is the default for Core-JS version 3.x.x.

NPM install --save core-js@3 Regenerator-Runtime # + host # + host

This way you don’t need to install @babel/polyfill, because when you install @babel/polyfill, it will automatically install Core-JS and Regenerator-Runtime. The core-js used by @babel/polyfill is already locked to version 2.x.x. The 2.x.x version of core-js does not have a directory of stable files, so installing @babel/polyfill and then introducing core-js/stable will return an error.

Cannot find module ‘core-js@3/library/fn/**’. For this type of error, check the core-js version number

This is similar to the example above, replacing the @babel/polyfill package with core-js@3 and regenerater-runtime NPM packages. The difference is as follows: the main code

import "core-js/stable";
import "regenerator-runtime/runtime";

var promise = Promise.resolve('hello babel')
console.log(promise)

HTML part

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial-scale =1.0"> <title> document< /title> </head> <body> <script SRC ="./dist/main.js"></ body> </ HTML >

Method 4: Import into the Webpack build configuration file entry

The Demo code repository address is tutor-polyfill05

There are three different entry files for this method, but to save space, I’ll just use @babel/polyfill

Const path = require('path') module.exports = {// entry: ['./ SRC /polyfill.js', './ SRC /index.js'] const path = require('path') module.exports = {// entry: ['./ SRC /polyfill.js', './ SRC /index.js'] ['@babel/polyfill', './ SRC /index.js'], // @babel/polyfill // entry: ['core-js/stable', 'regenerator-runtime/runtime', './ SRC /index.js'], // output: {filename: "./dist/main.js", path: path.resolve(__dirname, '') } }

Summary 1:

The above four methods are all manual patching methods. Manual patching puts the entire polyfill. Js into the project, which will affect the first screen time of the page due to the large polyfill file. This is not usually done in real projects.

Moving on to the second type of patching on demand compilation and patching on demand

Build on demand and patch on demand

The build-on-demand and patch-on-demand approach uses @babel/preset-env, which performs syntactic conversion and patching based on the target environment configuration table and the Babel configuration. For example, the following configuration:

{ "presets": [[" @ Babel/preset - env ", / / > @ Babel/env is @ the shorthand of Babel/preset - env {/ / https://babeljs.io/docs/en/babel-preset-env#targets "targets": "defaults" } ] ] }

How to use and configure the target environment profile is covered in the section “How to configure Babel(1) with your hands”. So let’s say it again for your reading

Target environment configuration table

If you’ve noticed, in a project initialized by scaffolding tools in frameworks like Vue or React, package.json has something called BrowsersList in it. Look at the following configuration meaning:

  "browserslist": [
    "> 2%",
    "not ie <= 8"
  ]

The above configuration means that the target environment is for browsers with a market share greater than 2% and does not consider Internet Explorer 8 or below.

The target environment repertoire specifies which browser or Node.js environment the code will run in, and determines the target environment in which the code will run.

Autoprefixer, PostCSS, etc., can automatically determine whether to add CSS prefixes (such as ‘-webkit-‘) based on BrowsersList configuration. The @babel/preset-env default also reads the BrowsersList configuration

If our @babel/preset-env doesn’t set any parameters, Babel will do the syntax conversion entirely according to BrowsersList configuration. Without BrowsersList, Babel would have converted all ES6 syntax to the ES5 version

In the following example, without adding browsersList or setting arguments to @babel/preset-env, the ES6 arrow function syntax is converted to the ES5 function definition syntax.

Compile the front:

let number1 = 10
let number2 = 20
const sum = (num1, num2) => num1 + num2

The compiled:

"use strict";

var number1 = 10;
var number2 = 20;

var sum = function sum(num1, num2) {
  return num1 + num2;
};

Next, under the root directory of the tutor-preset_env project in the Demo code repository, add the.browserSlistrc configuration as follows:

chrome 62

Note: Do not put quotation marks around strings

Run Babel./ SRC /index.js — out-of-file./dist/index.js to see the dist/index.js result

// new syntax let number1 = 10; let number2 = 20; const sum = (num1, num2) => num1 + num2;

The converted code is still the arrow function, and since Chrome62 already implements the arrow function syntax, it will not be converted to ES5’s function definition syntax.

Now demote Chrome62 in.browserSlistrc to Chrome36 and run Babel./ SRC /index.js — out-of-file./dist/index.js to see the dist/index.js result

var number1 = 10;
var number2 = 20;

var sum = function sum(num1, num2) {
  return num1 + num2;
};

The converted code is ES5’s function definition syntax, because Chrome36 does not support arrow function syntax.

The above shows how @babel/preset-env can degrade the syntax in the source code that is not supported in the target environment set by BrowsersList.

Learn how @babel/preset-env sets parameters to partially reference a feature API that is not supported by the target environment.

The latest @babel/preset-env parameter has a total of 15, but we’ll focus on four:

  • targets
  • useBuiltIns
  • modules
  • corejs

These four parameters are very important, the other parameters are rarely used, special scenarios need to refer to the official documentation

For presets, when there is no need to set a parameter to it, it simply puts the name of the preset in the array of presets, for example

{
  "presets": ["@babel/preset-env"]
}

If you need to set a parameter to a preset, the format is as follows:

{
  "presets": [ ["@babel/preset-env",{"targets": "defaults"}] ]
}

targets

Value range

  • String:string
  • An array of strings:Array<string>
  • String object {[string]: string} defaults to null object {}. Parameter entries are written the same way as BrowsersList, for example:

    {
    "presets": [
      [
        "@babel/preset-env",
        {
        "targets": {
           "chrome": "58",
           "ie": "11"
         }
        }
      ]
    ]
    }

    The target in @babel/preset-env has a higher priority than the browsersList configuration.

    It is recommended to use BrowsersList configuration and not to configure @babel/preset-env targets separately

useBuiltIns

Value range:

  • usage
  • entry
  • false

    The default value is: false

The value of useBuiltIns determines how @babel/preset-env handles polyfill

  • If this parameter is not set or if the default value is false, the polyfill will be introduced entirely.
  • When the value is “entry” or “usage”, the required polyfill is partially introduced based on the configured target environment

The entry parameter for useBuiltIns

The Demo code address @babel/preset-env defines the plug-in presets required by Babel, and Babel automatically loads all polyfills required by that configuration environment on demand, based on the supporting environment configured by BrowsersList. Note that the wording here is configuration environment requirement, not code requirement. What does that mean? It means that the browser environment needs to add it, even if you don’t use the API in your code, it will still add it to you. Isn’t that annoying? Let’s see how to configure it first

Babel configuration

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "entry"
      }
    ]
  ]
}

Install the NPM package

  npm install --save-dev @babel/cli @babel/core  @babel/preset-env
  npm install --save @babel/polyfill

Pre-compile code:

// After manually adding @babel/polyfill //useBuiltIns to entry, '@babel/polyfill' import '@babel/polyfill' const array1 = ['a', 'b', 'c', 'd', 'e'] console.log(array1.copyWithin(0, 3, 4)) console.log(array1.copyWithin(1, 3))

Specify the target environment browserslist configuration Because Array. Prototype. CopyWithin () in Chrome 45 version to support. Let’s set it to 90 and see the results

chrome 90

Run NPM Run Build

npm run build                                                                              

Chrome 90 is a new version of Chrome 90.

"use strict";

require("core-js/modules/web.timers.js");

require("core-js/modules/web.immediate.js");

require("core-js/modules/web.dom.iterable.js");

const array1 = ['a', 'b', 'c', 'd', 'e'];
console.log(array1.copyWithin(0, 3, 4));
console.log(array1.copyWithin(1, 3));

Babel references API features not supported by Chrome 90, introducing three Core-JS API completions. Also can see at the same time, because the Chrome 90 already support Array. Prototype. CopyWithin () feature, so there is no introduction of relevant API supplement the module.

Let’s change Chrome 40 in BrowsersList. Run NPM Run Build

Chrome 40:

require("core-js/modules/es6.array.copy-within.js"); . . require("core-js/modules/es6.typed.int8-array.js"); require("core-js/modules/es6.typed.uint8-array.js"); . . require("regenerator-runtime/runtime.js");

Because there are too many references, only parts are shown here


You can see
require("core-js/modules/es6.array.copy-within.js")

Consider: When useBuiltIns value entry, Babel automatically loads all polyfills required by the configuration environment on demand, based on the supporting environment configured by BrowsersList. In other words, the solution is to load whatever is needed by the target environment, regardless of whether the relevant features or APIs are used in the business code. So @babel/polyfill combined with @babel/preset-env + useBuiltins (Entry) + browsersList is popular, but not optimal.

For the above problem,@babel/preset-env + useBuiltins (usage) + browsersList. Note that useBuiltins is configured as usage, which can actually be used depending on the code. Analyze the AST (Abstract Syntax Tree) for more fine-grained on-demand references. This is the third and currently the best way of patching — patching on demand, based on browser characteristics. So how do we configure this

Patch as needed, depending on browser characteristics

The usage parameter for useBuiltIns

The Demo code address Babel configuration file is as follows:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage"
      }
    ]
  ]
}

Install the NPM package

npm install --save-dev @babel/cli @babel/core  @babel/preset-env
npm install --save @babel/polyfill

Specify the target environment browserslist configuration Because Array. Prototype. CopyWithin () in Chrome 45 version to support. We set it to 40 so that the polyfill can be introduced dynamically

chrome 40

Precompile code

@babel/polyfill // new const array1 = ['a', 'b', 'c', 'd', 'e'] console.log(array1. Copywithin (0, 3, 3)) 4)) console.log(array1.copyWithin(1, 3))

Compiled code

"use strict";

require("core-js/modules/es6.array.copy-within.js");
var array1 = ['a', 'b', 'c', 'd', 'e'];
console.log(array1.copyWithin(0, 3, 4));
console.log(array1.copyWithin(1, 3));

Using useBuiltIns: after “usage”, the same target environment, the compiled code only introduced the require (” core – js/modules/es6. Array. Copy – within. Js “); . When the ES6 feature API is missing in the target environment, Babel will introduce the Cre-JS API complement module.

Summarize the differences between the ‘entry’ and ‘usage’ values

  • entryThe mode needs to be added manually in the project entrypolyfill.usageDon’t need
  • usageMode is loaded on demand based on the code contentpolyfill.entryCan not be

corejs

USEBUILTINS will only take effect if set to ‘usage’ or ‘entry’. The value of this parameter entry can be either 2 or 3. If not set, the default is 2, and the following warning message will appear at compile time

> $NPM run build ⬡ 12.21.0 [± Master ●●] > select build /Users/liujianwei/Documents/personal_code/tutor-babel/packages/tutor-preset_env02 > babel ./src/index.js --out-file ./dist/index.js WARNING (@babel/preset-env): 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. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3 More info about useBuiltIns: https://babeljs.io/docs/en/babel-preset-env#usebuiltins More info about core-js: https://babeljs.io/docs/en/babel-preset-env#corejs

With the default value or 2, Babel compiles with the core-js@2 version (core-js2.x.x). Since some of the new APIs are only available in core-js@3, such as the Array.prototype.flat() method, we need to use the API module named “” to make up for it, so we set this to 3

When coreJS is 2, you need to install and import the core-js@2 version, or install @babel/polyfill directly. If CoreJS has a value of 3, you must install and import the core-js@3 version.

Let’s take a look at Array.prototype.flat() with a patched core-js@2 Demo code showing the address tutor-preset_env04

Install the NPM package

npm i -D @babel/cli @babel/core  @babel/preset-env  
npm i @babel/polyfill

Since Array.prototype.flat() is supported in Chrome 69, the target environment is set to Chrome 68

chrome 68

Babel configuration

{" presets ": [[" @ Babel/preset - env", {/ / "corejs" : 2, / / the default value is 2 "useBuiltIns" : "usage"}]]}

Pre-compile code:

const arr1 = [0, 1, 2, [3, 4]]

console.log(arr1.flat())

Compiled code:

"use strict";
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());

You can see that a flat polyfill is not added because there is no corresponding polyfill in the core-js@2 version.

Next, let’s change the configuration in.babelrc: Demo code repository tutor-preset_env05

{" presets ": [[" @ Babel/preset - env," {" corejs ": 3, / / specified 3 version" useBuiltIns ":" usage "}]]}

Install the NPM package

NPM i-d @babel/cli @babel/ core@babel /preset-env NPM i@babel /polyfill # specify core-js@3 NPM install --save

Compile results:

"use strict";

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

const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());

Can compare, at that time the require (” core – js/modules/es. Array. Flat. The js “); It was brought in.

At this point, you might be confused. Why is @babel/polyfill obsolete and still in use here? In fact, as mentioned above, @babel/polyfill is a fusion of core-js and regenerator-runtime. In this case, we can skip @babel/polyfill and go straight to core-js@3 and Regenerator-Runtime. A word or two on the code display:

DEMO code repository tutor-preset_env06

Install the NPM package

npm install --save-dev @babel/cli @babel/core  @babel/preset-env  
npm install --save core-js@3  

No installation shown
regenerator-runtimeBecause in the installation
@babel/preset-envThe time,
regenerator-runtimeIt has already been installed as a dependency

{" presets ": [[" @ Babel/preset - env," {" corejs ": 3, / / specified 3 version" useBuiltIns ":" usage "}]]}

Install the NPM package

NPM i-d @babel/cli @babel/ core@babel /preset-env NPM i@babel /polyfill # specify core-js@3 NPM install --save

Compile results:

"use strict";

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

const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());

Using core-js@3 directly will have the same effect as @babel/polyfill+ light.

modules

Systemjs umd “amd” | “” |” “|” commonjs “|” CJS “| |” auto “false, defaults to” auto “the default value is auto. This item sets whether or not to change ES6’s modularization syntax to another modularization syntax.

There are two common types of modularity syntax:

  • ES6 module French method –import and export
  • CommonJS module syntax –require and module.exports

    In the previous examples, there was no settingmodules, after the code is compiled,importBe replaced withrequire.

If you change the parameter entry to false, no ES6 modularization changes will be made and the module will be imported using import. The Demo code repository address tutor-preset_env07 Babel is configured as follows

{"presets": [["@babel/preset-env", {" coreJS ": 3, // specify version 3 "useBuiltIns": "usage", "modules":false}]]}

Install the NPM

 npm i @babel/core @babel/cli @babel/preset-env
 npm i core-js@3 

Compiled results:

import "core-js/modules/es.array.flat.js";
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());

What are the benefits of using ES6 modular syntax?

In the use of Webpack and other packaging tools, static analysis can be carried out, so that tree-shaking and other optimization measures can be done

The above content configuration of Babel is sufficient for business type projects.

If you want to distribute an NPM package for others to use, you also need to learn about @babel/runtime and @babel/plugin-transform-runtime in the next section.

The last

Pay attention to my public account — front-end XueClub or Nuggets account, wonderful not to be missed