Original address: github.com/yinxin630/b… Technical exchange group: fiora.suisuijiang.com/

Let’s talk about Babel and Polyfill

The ES2015 standard has been around for three years, and we write ES2015(or later) code in our projects, but the environment (browser) in which the code ends up running is usually out of control, so we need to compile ES2015 into a lower version of the code to ensure that all target environments run

Babel is a tool for compiling from a higher version to a lower version. Without additional plug-ins, Babel simply transforms ES2015 syntax (for of, for example), while new classes/methods in ES2015 (Set, [1, 2].findIndex() will remain unchanged

This is where polyfill is needed. You need to add @babel/polyfill at the beginning of your project entry file. In projects, however, only a limited amount of Polyfill content is usually used, and the latest @babel/ Polyfill package is 81.2K (Gzipped 27.7K) in size

Can you just polyfill what you use in your code?

Suppose you have the following source code:

const set = new Set(a);// ES6 Set
set.add(1);
set.add(2);
set.add(3);

const arr = [1.2.3]; // ES6 for.. of
for (const a of arr) {
    console.log(a);
}

console.log(arr.findIndex(x= > x === 2));  // ES6 Array.prototype.findIndex
Copy the code

Next, try different polyfill schemes

@babel/plugin-transform-runtime

Babeljs. IO/docs/en/bab…

The first is to use the TransFrom-Runtime plug-in, which can polyfill only class/static methods used in the code, but does not apply to methods added to the prototype chain

NOTE: Instance methods such as “foobar”.includes(“foo”) will not work since that would require modification of existing built-ins (you can use @babel/polyfill for that).

Add the Babel configuration

// Preset {"presets": [["@babel/preset-env",]], "plugins": [["@babel/preset- runtime", {"corejs": presets": [["@babel/preset-env",]], "plugins": [["@babel/preset- runtime", {"corejs": 2, "helpers": true, "regenerator": true, "useESModules": false } ] ] }Copy the code

The compiled:

"use strict";

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

var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));

var set = new _set.default();
set.add(1);
set.add(2);
set.add(3);
var arr = [1.2.3];

for (var _i = 0; _i < arr.length; _i++) {
  var a = arr[_i];
  console.log(a);
}

console.log(arr.findIndex(function (x) {
  return x === 2;
}));
Copy the code

The compiled code just introduces the Set implementation, but findIndex() doesn’t polyfill. If you’re sure you’re not going to use any of the new methods in the prototype chain, then @babel/ plugin-transform-Runtime might be a good choice

@babel/preset-env + useBuiltIns

Babeljs. IO/docs/en/bab…

@ Babel/preset – env support you configure the target environment, its useBuiltIns option, there are three optional value “usage” | | “entry” false (default)

This option adds direct references to the core-js module as bare imports. Thus core-js will be resolved relative to the file itself and needs to be accessible. You may need to specify core-js@2 as a top level dependency in your application if there isn’t a core-js dependency or there are multiple versions.

useBuiltIns: ‘entry’

This option requires the introduction of @babel/ Polyfill into the project, and Babel automatically breaks @babel/ Polyfill into smaller polyfill references that are only needed by the target environment

NOTE: Only use require(“@babel/polyfill”); once in your whole app. Multiple imports or requires of @babel/polyfill will throw an error since it can cause global collisions and other issues that are hard to trace. We recommend creating a single entry file that only contains the require statement.

Start by adding a Polyfill reference to the first line of the source code

import '@babel/polyfill'
Copy the code

Modify the Babel configuration

// Preset {"presets": [["@babel/preset-env", {"targets": "Chrome 40", "useBuiltIns": "entry"}]]}Copy the code

The compiled:

"use strict";

require("core-js/modules/es6.array.copy-within");

require("core-js/modules/es6.array.fill");

require("core-js/modules/es6.array.find");

require("core-js/modules/es6.array.find-index");

require("core-js/modules/es6.array.from");

require("core-js/modules/es7.array.includes");

require("core-js/modules/es6.array.of");

require("core-js/modules/es6.array.sort");

require("core-js/modules/es6.array.species");

require("core-js/modules/es6.date.to-primitive");

require("core-js/modules/es6.function.has-instance");

require("core-js/modules/es6.map");

require("core-js/modules/es6.number.constructor");

require("core-js/modules/es6.object.assign");

require("core-js/modules/es7.object.define-getter");

require("core-js/modules/es7.object.define-setter");

require("core-js/modules/es7.object.entries");

require("core-js/modules/es6.object.freeze");

require("core-js/modules/es6.object.get-own-property-descriptor");

require("core-js/modules/es7.object.get-own-property-descriptors");

require("core-js/modules/es6.object.get-prototype-of");

require("core-js/modules/es7.object.lookup-getter");

require("core-js/modules/es7.object.lookup-setter");

require("core-js/modules/es6.object.prevent-extensions");

require("core-js/modules/es6.object.is-frozen");

require("core-js/modules/es6.object.is-sealed");

require("core-js/modules/es6.object.is-extensible");

require("core-js/modules/es6.object.seal");

require("core-js/modules/es7.object.values");

require("core-js/modules/es6.promise");

require("core-js/modules/es7.promise.finally");

require("core-js/modules/es6.reflect.apply");

require("core-js/modules/es6.reflect.construct");

require("core-js/modules/es6.reflect.define-property");

require("core-js/modules/es6.reflect.delete-property");

require("core-js/modules/es6.reflect.get");

require("core-js/modules/es6.reflect.get-own-property-descriptor");

require("core-js/modules/es6.reflect.get-prototype-of");

require("core-js/modules/es6.reflect.has");

require("core-js/modules/es6.reflect.is-extensible");

require("core-js/modules/es6.reflect.own-keys");

require("core-js/modules/es6.reflect.prevent-extensions");

require("core-js/modules/es6.reflect.set");

require("core-js/modules/es6.reflect.set-prototype-of");

require("core-js/modules/es6.regexp.constructor");

require("core-js/modules/es6.regexp.flags");

require("core-js/modules/es6.regexp.match");

require("core-js/modules/es6.regexp.replace");

require("core-js/modules/es6.regexp.split");

require("core-js/modules/es6.regexp.search");

require("core-js/modules/es6.regexp.to-string");

require("core-js/modules/es6.set");

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

require("core-js/modules/es7.symbol.async-iterator");

require("core-js/modules/es6.string.code-point-at");

require("core-js/modules/es6.string.ends-with");

require("core-js/modules/es6.string.from-code-point");

require("core-js/modules/es6.string.includes");

require("core-js/modules/es7.string.pad-start");

require("core-js/modules/es7.string.pad-end");

require("core-js/modules/es6.string.raw");

require("core-js/modules/es6.string.repeat");

require("core-js/modules/es6.string.starts-with");

require("core-js/modules/es6.typed.array-buffer");

require("core-js/modules/es6.typed.int8-array");

require("core-js/modules/es6.typed.uint8-array");

require("core-js/modules/es6.typed.uint8-clamped-array");

require("core-js/modules/es6.typed.int16-array");

require("core-js/modules/es6.typed.uint16-array");

require("core-js/modules/es6.typed.int32-array");

require("core-js/modules/es6.typed.uint32-array");

require("core-js/modules/es6.typed.float32-array");

require("core-js/modules/es6.typed.float64-array");

require("core-js/modules/es6.weak-map");

require("core-js/modules/es6.weak-set");

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

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

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

require("regenerator-runtime/runtime");

var set = new Set(a); set.add(1);
set.add(2);
set.add(3);
var arr = [1.2.3];

for (var _i = 0; _i < arr.length; _i++) {
  var a = arr[_i];
  console.log(a);
}

console.log(arr.findIndex(function (x) {
  return x === 2;
}));
Copy the code

The compiled code automatically introduces everything that Chrome 40 doesn’t support, including Set and findIndex(), and it doesn’t analyze what the source code uses

Modify targets to Chrome 60.

"use strict";

require("core-js/modules/es6.array.sort");

require("core-js/modules/es7.object.define-getter");

require("core-js/modules/es7.object.define-setter");

require("core-js/modules/es7.object.lookup-getter");

require("core-js/modules/es7.object.lookup-setter");

require("core-js/modules/es7.promise.finally");

require("core-js/modules/es7.symbol.async-iterator");

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

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

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

const set = new Set(a); set.add(1);
set.add(2);
set.add(3);
const arr = [1.2.3];

for (const a of arr) {
  console.log(a);
}

console.log(arr.findIndex(x= > x === 2));
Copy the code

Because Chrome 60 already supports Set and findIndex(), Polyfill does not include them

useBuiltIns: ‘usage’

This option is experimental at the moment, so let’s see what it looks like in a package

The first step is to remove the import ‘@babel/polyfill’ from the source code

Modify Babel configuration:

{
    "presets": [["@babel/preset-env",
            {
                "targets": "Chrome 40"."useBuiltIns": "usage"}}]]Copy the code

The compiled:

"use strict";

require("core-js/modules/es6.array.find-index");

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

require("core-js/modules/es6.set");

var set = new Set(a); set.add(1);
set.add(2);
set.add(3);
var arr = [1.2.3];

for (var _i = 0; _i < arr.length; _i++) {
  var a = arr[_i];
  console.log(a);
}

console.log(arr.findIndex(function (x) {
  return x === 2;
}));
Copy the code

Wow, it looks like this is what I need! But it is analysis to I use the Array. The protoptype. FindIndex () to add polyfill? Let’s do an experiment

Modify source code:

String.prototype.findIndex = function() {}
const str = ' ';
str.findIndex(); / / call the String. The prototype. FindIndex
Copy the code

Instead of calling the Array prototype chain’s findIndex, I called the String prototype chain’s findIndex

The compiled:

"use strict";

require("core-js/modules/es6.array.find-index");

String.prototype.findIndex = function () {};

var str = ' ';
str.findIndex();
Copy the code

😂 turns out to be a direct matching method name, with the polyfill of the same name added

Conclusion useBuiltIns

UseBuiltIns: ‘Entry’ is polyfilled according to the target environment, regardless of whether it is used in the code, it is guaranteed to be available in the target environment

UseBuiltIns: ‘usage’ is an experimental configuration for now, it analyzes code calls, but for methods on the prototype chain just match by method name, resulting in a smaller polyfill volume. But it does not analyze the contents of the NPM package that the code depends on, and if an NPM package requires some polyfills, those polyfills are not packaged

Why can’t the methods on the prototype chain be polyfilled as needed according to whether they are used or not?

Because of the dynamic typing nature of JavaScript, some variable/instance types are only determined at runtime, whereas Babel is just a static compilation of code, So it can’t be sure findIndex () whether Array. Protoptype. FindIndex (), such as:

fetch('/api')
.then(res= > res.json())
.then(data= > data.findIndex)
Copy the code

The type of data is determined by what is returned by the runtime interface, so Babel cannot polyfill prototype chain methods on demand

TypeScript has static typing. Can you polyfill it on demand?

The conclusion is no! A discussion of polyfill can be found at github.com/Microsoft/T…

TypeScript can specify libraries to depend on with the –lib argument, and can polyfill dependent libraries with ts-polyfill, but dependencies cannot be specified in detail to a method, only esNext-.array

If you have to just polyfillSet 和 findIndex?

You can manually import the corresponding implementation in Core-JS, for example:

import 'core-js/modules/es6.set.js';
import 'core-js/modules/es6.array.find-index.js';
Copy the code

This is not recommended, unless you are looking for a minimum polyfill size, you must know exactly what is being used in your project. But in real projects, especially in multi-person projects, it is often difficult to control