Нужны ли мне полифиллы при использовании Babel? ? ?

Babel

Два дня назад коллега задал мне такой вопрос на собеседовании, и интервьюер подошел и спросил его: «А нужны ли мне еще полифиллы при использовании babel в проекте?» Сначала он растерялся, как он мог еще так выйти на вопрос о картах, по основным принципам собеседования, ответ должен быть обязательным, иначе как можно задавать дальнейшие вопросы. Поэтому он сказал «да». Когда интервьюер копнул глубже, он, наконец, не мог больше этого выносить. На самом деле, большинство скаффолдов, используемых в обычном процессе разработки, являются готовыми скаффолдами, редко кто внимательно смотрит на конфигурацию babel, не говоря уже о том, чтобы копаться глубоко в использовании babel. Итак, сегодня я дам вам хороший ответ на этот вопрос.

Первое примечание: Babel, о котором мы упоминали позже, основан на версии 7.10.0.

Что такое Вавилон

Выкинь определение официального китайского документа

Babel — это цепочка инструментов, в основном используемая для преобразования кода версии ECMAScript 2015+ в обратно совместимый синтаксис JavaScript, чтобы его можно было запускать в текущих и более старых браузерах или других средах. Вот что Babel может сделать для вас:

  • Преобразование синтаксиса
  • Добавьте недостающие функции в целевую среду с помощью полифилла (через@babel/polyfillмодуль)
  • Преобразование исходного кода (codemods)
  • Более! (проверьте этивидеоБыть вдохновленным)

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

Реальность такова, что babel просто предоставляет «платформу», позволяющую более функциональным плагинам устанавливаться на мою платформу, именно эти плагины предоставляют возможность конвертировать код версии ECMAScript 2015+ в обратно совместимый синтаксис JavaScript.

Так как же он это сделал? Здесь нельзя не упомянуть знаменитый AST.

Как работает Вавилон

Рабочий процесс Babel делится на три этапа:разбор, преобразование, печать

  • Babylon внутри babel на этапе синтаксического анализа отвечает за преобразование кода es6 в абстрактное синтаксическое дерево после синтаксического анализа и лексического анализа.

  • Преобразование интерьера сцены Babel-Traverse, отвечающая за абстрактные синтаксические преобразования дерева

  • Babel-генератор внутри стадии печати отвечает за генерацию соответствующего кода

Преобразование второго шага является высшим приоритетом. Механизм плагинов Babel также играет роль в этом шаге. Плагины работают здесь, конвертируют его в новый AST, а затем передают его в Babel-генератор на третьем шаге. Итак, как мы сказали выше, если эти плагины не входят в платформу, то «платформа» Babel не имеет никаких возможностей. Именно так:

const babel = code => code;

Так что можно с уверенностью сказать, что ответ «нужно». Требуются не только полифилы, но и множество плагинов.

Ниже мы проиллюстрируем на примерах и какие плагины необходимы для идеального преобразования кода ECMAScript 2015+ в обратно совместимый синтаксис JavaScript.

Разница между preset-env, polyfill, plugin-transform-runtime

Теперь давайте создадим пример с помощью npm init -y , затем установим @babel/cli и @babel/core. по командеbabel index.js --out-file compiled.jsСкомпилируйте индексный файл в скомпилированный.js с помощью babel

// index.js
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

Без всяких плагинов

Чтобы подтвердить вышеизложенное утверждение, мы сначала тестируем ситуацию без каких-либо плагинов, и результаты следующие:

//compiled.js
const fn = () => {
  console.log("wens");
};

const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

В скомпилированном файле нет изменений, что подтверждает наше заявление выше. Далее добавляем плагины.

Прежде чем присоединиться к тестированию плагинов, нам нужно знать некоторые предварительные знания.Babel делит код версии ECMAScript 2015+ на два случая:

  • Уровень синтаксиса: let, const, class, функция стрелки и т. д. Они должны быть переведены во время построения, что относится к переводу на уровне синтаксиса.
  • Слой методов API: Promise, include, map и т. д. Это новые методы на прототипе глобального или Object, Array и т. д. Их можно переопределить соответствующим методом es5.

Перевод Babel для этих двух случаев отличается, и нам нужно дать соответствующую конфигурацию.

добавить предустановленную среду

Приведенный выше пример является const, стрелочные функции относятся к уровню синтаксиса, а обещание и карта относятся к уровню методов API Теперь давайте добавим preset-env, чтобы увидеть эффект.

// babel.config.js
module.exports = {
  presets: ["@babel/env"],
  plugins: []
};

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

//compiled.js
"use strict";
var fn = function fn() {
  console.log("wens");
};

var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

Конечно же, он понижен с грамматического уровня. Так как же с этим бороться на уровне API? Ниже мы присоединяемся@bable/polyfill

добавить полифилл

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

Установите @bable/polyfill в проект, а затем импортируйте его в файл index.js.

// index.js
import "@bable/polyfill";
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

Скомпилируйте еще раз и посмотрим на результат

// compiled.js
"use strict";

require("@bable/polyfill");

var fn = function fn() {
  console.log("wens");
};

var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

Если нет других изменений, то есть дополнительная строка require("@bable/polyfill"), по сути, здесь представлены все полифиллы в этой библиотеке, как и все методы lodash в нашем проекте. Это поддерживает методы Promise и map.

Внимательные студенты определенно сочтут это немного «глупым», lodash обеспечивает загрузку по запросу, вы представили все это сразу, но мне нужны только Promise и карта. Не паникуйте, посмотрим ниже.

Настроить useBuiltIns

выше мы проходимimport "@bable/polyfill"способ добиться «сглаживания» на уровне API. Однако, начиная с версии babel 7.4.0, этот метод официально не рекомендуется. Потому что введение @bable/polyfill эквивалентно введению в код следующих двух библиотек:

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

Это означает, что он не только не может быть загружен по запросу, но и загрязняется глобальное пространство. Потому что он делает это, добавляя методы к прототипам глобальных объектов и встроенных объектов.

Поэтому babel решил передать работу этих двух человек упомянутому выше @babel/env, который не только не загрязняет глобально, но и поддерживает загрузку по требованию. Теперь давайте посмотрим на измененную конфигурацию.

// index.js
// 去掉了polyfill
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

// webpack.config.js
module.exports = {
  presets: [
    [
      "@babel/env",
      {
        useBuiltIns: "usage", // 实现按需加载
        corejs: { 
          version: 3, 
          proposals: true 
        }
      }
    ]
  ],
  plugins: []
};

Настроив @babel/env со свойствами useBuiltIns и corejs, мы реализуем загрузку методов полифилла по требованию. Все элементы конфигурации см.официальная документация

// compiled.js
"use strict";

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

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

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

var fn = function fn() {
  console.log("wens");
};

var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

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

Присоединяйтесь к @babel/plugin-transform-runtime

Реконструкция выше

// index.js
class Person {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(this.name);
  }
}

Преобразуйте только класс Person, давайте посмотрим на файл преобразования long

// compiled.js 
"use strict";

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

require("core-js/modules/es.object.define-property");

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Person = /*#__PURE__*/function () {
  function Person(name) {
    _classCallCheck(this, Person);

    this.name = name;
  }

  _createClass(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);

  return Person;
}();

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

среда выполнения плагина-преобразования находится здесь.

Три функции _classCallCheck, _defineProperties и _createClass, показанные выше, называются вспомогательными функциями, то есть функциями, помогающими Babel на этапе компиляции.

Когда используется плагин plugin-transform-runtime, встроенные вспомогательные функции, добавленные в файл во время трансляции babel, могут быть единообразно изолированы во вспомогательном модуле, предоставляемом babel-runtime.При компиляции он загружается непосредственно из вспомогательного модуля, а не в каждая Вспомогательные функции многократно определены в файле, тем самым уменьшая размер пакета.Давайте посмотрим на эффект:

// webpack.config.js
module.exports = {
  presets: [
    [
      "@babel/env",
      {
        useBuiltIns: "usage",
        corejs: { version: 3, proposals: true }
      }
    ]
  ],
  plugins: ["@babel/plugin-transform-runtime"]
};
// compiled.js
"use strict";

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

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

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

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

var Person = /*#__PURE__*/function () {
  function Person(name) {
    (0, _classCallCheck2["default"])(this, Person);
    this.name = name;
  }

  (0, _createClass2["default"])(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);
  return Person;
}();

Он отлично решает проблему избыточности кода. Как вы думаете, это закончилось, еще нет. Учащиеся, внимательно посмотревшие сюда, должны были заметить, что, хотя вышеприведенное использованиеuseBuiltInsЭлемент конфигурации реализует ссылку на poilyfill по требованию, но он по-прежнему имеет загрязнение глобальной переменной, как и этот код, который переписывает метод прототипа массива, вызывая глобальное загрязнение.

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

Наконец, снова измените файл конфигурации babel.

// webpack.config.js
module.exports = {
  presets: ["@babel/env"],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: { version: 3 }
      }
    ]
  ]
};

Мы видим, что соответствующие параметры @babel/env удалены, а параметр corejs добавлен в plugin-transform-runtime, окончательный сконвертированный файл больше не будет иметь требуемый метод полифилла.

// compiled.js
"use strict";

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

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));

var Person = /*#__PURE__*/function () {
  function Person(name) {
    (0, _classCallCheck2["default"])(this, Person);
    this.name = name;
  }

  (0, _createClass2["default"])(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);
  return Person;
}();

Таким образом, подключаемый модуль plugin-transform-runtime реализует следующие две важные функции с помощью babel-runtime.

  • Повторное использование вспомогательных функций для устранения избыточности кода при переводе уровня синтаксиса.
  • Решите загрязнение глобальной переменной в API-слое перевода.

конец

Я наконец закончил его писать, и от прочтения документа до написания статьи прошло два дня. Я надеюсь, что студенты, прочитавшие статью, смогут получить столько же, сколько и я~~