Может ли babel анализировать код и заполнять его по мере необходимости?

JavaScript Babel

Оригинальный адрес:GitHub.com/Yunxin630/Нет…
Группа технического обмена:fiora.suisuijiang.com/

Давайте сначала поговорим о Babel и Polyfill.

Стандарт ES2015 выпущен уже три года, мы будем писать код ES2015 (или более поздней версии) в проекте, но среда (браузер), в которой в итоге будет выполняться код, обычно неуправляема, поэтому ES2015 нужно компилировать в низкоуровневый код, чтобы убедиться, что все целевые среды могут работать

Babel — это инструмент, используемый для компиляции старшей версии в младшую.Без настройки дополнительных плагинов Babel — это просто синтаксис ES2015 (например,for of) преобразование, в то время как классы/методы добавлены в ES2015 (например,Setили[1, 2].findIndex()) останется как есть

В это время нужен полифилл, который нужно ввести в начале файла входа в проект.@babel/polyfill, Но в проекте обычно используется только ограниченный полифилл-контент, и последняя версия@babel/polyfillОбъем упаковки имеет размер 81,2 кк (GZARD 27,7К)

Так можно просто полифилл, что используется в коде?

Предположим, у вас есть следующий исходный код:

const set = new Set(); // 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

Затем попробуйте разные схемы полифилла

@babel/plugin-transform-runtime

Вавилон — это .IO/docs/en/daddy…

Первый — использовать подключаемый модуль времени выполнения Transfrom, который может полифиллировать только классовые/статические методы, используемые в коде, но недействителен для новых методов в цепочке прототипов.

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).

Добавить конфигурацию Babel

// babel 配置
{
    "presets": [
        [
            "@babel/preset-env",
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 2,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            }
        ]
    ]
}

После компиляции:

"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;
}));

Скомпилированный код представляет только реализацию Set, ноfindIndex()нет полифилла Если вы уверены, что не будете использовать ни один из методов, добавленных в цепочку прототипов, то@babel/plugin-transform-runtimeбыл бы хороший выбор

@babel/preset-env + useBuiltIns

Вавилон — это .IO/docs/en/daddy…

@babel/preset-envподдерживает вас в настройке целевой среды, ееuseBuiltInsвариант с тремя необязательными значениями «использование» | «запись» | ложь (по умолчанию)

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'

Эту опцию необходимо внедрить в проект@babel/polyfill, babel автоматически@babel/polyfillРазбейте на более мелкие ссылки полифилла, которые нужны только целевой среде.

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.

Сначала добавьте ссылку на полифилл в первую строку исходного кода.

import '@babel/polyfill'

Изменить конфигурацию Babel

// babel 配置
{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": "Chrome 40",
                "useBuiltIns": "entry"
            }
        ]
    ]
}

После компиляции:

"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();
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;
}));

Все, что Chrome 40 не поддерживает, автоматически добавляется скомпилированным кодом, в том числеSetа такжеfindIndex(), он не анализирует, что использует исходный код

Попробуйте изменить цели на 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();
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));

Поскольку Chrome 60 уже поддерживаетSetа такжеfindIndex(), поэтому содержимое полифилла их не включает

useBuiltIns: 'usage'

Этот вариант пока экспериментальный, посмотрим как он выглядит в упаковке

Сначала удалите исходный кодimport '@babel/polyfill'

Измените конфигурацию Babel:

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

После компиляции:

"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();
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;
}));

Вау, похоже, это то, что мне нужно! Но он разобран до такой степени, что я используюArray.protoptype.findIndex()Вы только что добавили полифилл? Давайте попробуем

Измените исходный код:

String.prototype.findIndex = function() {}
const str = '';
str.findIndex(); // 调用 String.prototype.findIndex

На этот раз я не вызывал findIndex цепочки прототипов Array, а вызывал findIndex цепочки прототипов String, которую я реализовал.

После компиляции:

"use strict";

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

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

var str = '';
str.findIndex();

😂 Получается, что это имя метода прямого сопоставления, и добавляется одноименный полифилл

вывод useBuiltIns

useBuiltIns: 'entry'Он полифиллируется в соответствии с целевой средой, независимо от того, используется ли он в коде, можно гарантировать его доступность в целевой среде.

useBuiltIns: 'usage'Это все еще экспериментальная конфигурация, она будет анализировать вызовы кода, но для методов в цепочке прототипов она совпадает только по имени метода, и можно получить меньший объем полифилла, но не будет анализировать содержимое пакет npm, от которого зависит код. Если для определенного пакета npm требуются некоторые полифиллы, и эти полифилы не будут упакованы в

Почему нельзя использовать методы в цепочке прототипов, а затем полифилить по мере необходимости?

В основном из-за динамического типа JavaScript тип некоторых переменных/экземпляров может быть определен только во время выполнения, а babel только статически компилирует код, поэтому его нельзя определить.findIndex()ЭтоArray.protoptype.findIndex(), например:

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

Тип данных определяется содержимым, возвращаемым интерфейсом времени выполнения, поэтому Babel не может реализовать методы цепочки прототипов для полифилла по требованию.

TypeScript имеет статическую типизацию, можно ли его заполнить по требованию?

Вывод - нет!Обсуждение полифилла можно посмотретьGitHub.com/Microsoft/T…

TypeScript может использовать--libПараметр указывает библиотеку, от которой зависит, сts-polyfillВы можете полифиллировать зависимые библиотеки, но вы не можете подробно указать метод при указании зависимостей, толькоESNext.Array

Если вам нужно просто полифиллSetа такжеfindIndexШерстяная ткань?

можно импортировать вручнуюcore-jsСоответствующая реализация, например:

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

Эта практика не рекомендуется, если вы не гонитесь за наименьшим размером полифилла, вы должны четко знать, какой контент используется в проекте, но в реальных проектах, особенно проектах, разработанных несколькими людьми, обычно трудно контролировать