Самый понятный и понятный анализ конфигурации Babel в истории.

Babel

Название вечеринка хахаха ~~~

исходный адрес

Я считаю, что многие люди такие же, как и автор раньше.Конфигурация babel копируется и вставляется из Интернета или с помощью готовых скаффолдов.Хотя это работает, я надеюсь, что все могут знать, почему.Поэтому в этой статье будет делаться конфигурация о babel (babel@7) Более полный обзор.

синтаксис и API

Контент, добавленный es6, можно разделить на две части: грамматика и API, это очень важно понимать Новые грамматики, такие как стрелочные функции, деструктурирование и т. д.:

const fn = () => {}

const arr2 = [...arr1]

Новые API, такие как Map, Promise и т. д.:

const m = new Map()

const p = new Promise(() => {})

@babel/core

@ Babel / Core, вы можете увидеть из названия, что это ядро ​​Бабела, без него, поэтому установите этот пакет первым

npm install @babel/core

Его роль заключается в преобразовании кода в соответствии с нашим конфигурационным файлом, который обычно.babelrc(статические файлы) илиbabel.config.js(программируемый), здесь с.babelrcНапример, создайте пустой файл в корневом каталоге проекта с именем.babelrc, а затем создайте файл js (test.js) для тестирования:

/* test.js */
const fn = () => {}

Здесь мы устанавливаем@babel/cliчтобы иметь возможность использовать babel из командной строки

npm install @babel/cli

После завершения установки выполните компиляцию babel и войдите в командную строку.

npx babel test.js --watch --out-file test-compiled.js

Оказалось, что содержимое test-compiled.js по-прежнему является стрелочной функцией es6.Не волнуйтесь, наш .babelrc еще не написал конфигурацию.

Плагины и пресеты

Now, out of the box Babel doesn't do anything. It basically acts like const babel = code => code; by parsing the code and then generating the same code back out again. You will need to add plugins for Babel to do anything.

Выше это отрывок с официального сайта Babel.Можно понять,что Babel основан на архитектуре плагинов.Если вы не предоставите никаких плагинов,то Babel не будет ничего делать,то есть то,что вы вводите и выводите еще что. Итак, теперь нам просто нужно предоставить плагин функции стрелки, если мы хотим преобразовать функцию обрезки в функцию es5:

/* .babelrc */
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]    
}

Преобразованный test-compiled.js:

/* test.js */
const fn = () => {}

/* test-compiled.js */
const fn = function () {}

Так что, если я хочу использовать синтаксис деструктурирования es6? Это очень просто, просто добавьте плагин деструктуризации:

/* .babelrc */
{
  "plugins": [
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-destructuring"
  ]    
}

Проблема в том, что существуют так много грамматиков, которые необходимо преобразовать, и это слишком хлопотно, чтобы добавить плагины один за другим. К счастью, Babel предоставляетpresets, его можно понимать как набор плагинов, что избавляет нас от необходимости вводить плагины один за другим. Официальный предоставляет множество пресетов, таких какpreset-env(набор плагинов, которые обрабатывают синтаксис спецификации ES6+),preset-stage(обработка коллекций плагинов, которые все еще находятся в синтаксисе предложения),preset-react(набор плагинов, которые имеют дело с синтаксисом реакции) и т. д., здесь мы в основном представляем следующиеpreset-env:

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

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

Вышеупомянутый официальный сайт babelpreset-envвведение, примерно означает, чтоpreset-envОн позволяет использовать синтаксис es6 для написания кода и преобразовывать только тот код, который необходимо преобразовать. по умолчаниюpreset-envНичего настраивать не нужно.В настоящее время он преобразует весь код es6+, но мы можем предоставить элемент конфигурации target для указания рабочей среды:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "targets": "ie >= 8"
    }]
  ]    
}

На данный момент будут преобразованы только синтаксисы, которые не поддерживаются браузерами выше ie8.Глядя на наш файл test-compiled.js, все в порядке:

/* test.js */
const fn = () => {}
const arr1 = [1, 2, 3]
const arr2 = [...arr1]


/* test-compiled.js */
var fn = function fn() {};
var arr1 = [1, 2, 3];
var arr2 = [].concat(arr1);

@babel/polyfill

Теперь давайте немного изменим test.js:

/* test.js */
const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
var fn = function fn() {};
new Promise(function () {});

Мы обнаружили, что обещания не обращаются, что! IE8 также поддерживает обещания? Это невозможно.... Помните, что содержание, добавленное к спецификации ES6 + в начале этой статьи, включает новые грамматики и новые API. Новые грамматики могут быть преобразованы с Babel, но новые API могут быть проведены только в полифах, поэтому нам нужно только установить их@babel/polyfill, Затем следуют простые модификации test.js:

/* test.js */
import '@babel/polyfill'

const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
import '@babel/polyfill';

var fn = function fn() {};
new Promise(function () {});

Теперь код может отлично работать в среде ie8, но все еще есть проблема:@babel/polyfillРазмер этого пакета слишком велик, нам нужен только Promise, если мы сможем полифилить его по мере необходимости. Какое совпадение,preset-envПросто предоставил эту функцию:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "entry",
      "targets": "ie >= 8"
    }]
  ]    
}

мы просто даемpreset-envдобавить одинuseBuiltInsЭлементы конфигурации могут быть, значение может бытьentryа такжеusage, если этоentry, добавит в запись полифиллы, которые все браузеры выше ie8 и выше не поддерживают API, а именно:

/* test.js */
import '@babel/polyfill'

const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
import "core-js/modules/es6.array.copy-within";
import "core-js/modules/es6.array.every";
import "core-js/modules/es6.array.fill";
...   //省略若干引入
import "core-js/modules/web.immediate";
import "core-js/modules/web.dom.iterable";
import "regenerator-runtime/runtime";

var fn = function fn() {};
new Promise(function () {});

Осторожно, вы обнаружите, что после преобразованияimport '@babel/polyfill'Ушел, но куча большеimport 'core-js/...'Содержание, на самом деле,@babel/polyfillСам пакет без содержимого, это зависит отcore-jsа такжеregenerator-runtimeЭти два пакета, эти два пакета обеспечивают среду выполнения спецификации ES6+. Поэтому, когда нам не нужны полифиллы по запросу, импортируйте их напрямую.@babel-polyfillНа линии, это будетcore-jsа такжеregenerator-runtimeВсе импортное, когда нам нужно полифилить по требованию, нам нужно только настроитьuseBuiltInsВот и все, оно будет автоматически импортировать на спрос на основе целевой средыcore-jsа такжеregenerator-runtime.

упомянутый ранееuseBuiltInsЗначение также может бытьusage, его функция более мощная, он будет сканировать ваш код, только какой новый API использует ваш код, он представит соответствующий полифилл:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "usage",
      "targets": "ie >= 8"
    }]
  ]    
}

Преобразованный test-compiled.js будет соответственно сильно упрощен:

/* test.js */
const fn = () => {}
new Promise(() => {})

/* test-compiled.js */
import "core-js/modules/es6.promise";
import "core-js/modules/es6.object.to-string";

var fn = function fn() {};
new Promise(function () {});

К сожалению, эта функция все еще является экспериментальной, поэтому используйте ее с осторожностью.

На самом деле, если вы пишете приложение, приведенной выше конфигурации для babel почти достаточно, вам может понадобиться добавить какую-то конкретную цель.Pluginа такжеPreset, например, в реактивном проекте вам нужноpresetsДобавить к@babel/preset-react, если вы хотите использовать функцию динамического импорта, вам нужноpluginsДобавить к@babel/plugin-syntax-dynamic-importПодождите, они не повторяются. Если вы пишете публичную библиотеку или фреймворк, упомянутые ниже моменты также могут потребовать вашего внимания.

@babel/runtime

Иногда преобразование синтаксиса относительно сложно, и могут потребоваться некоторые вспомогательные функции, такие как преобразование классов es6:

/* test.js */
class Test {}


/* test-compiled.js */
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Test = function Test() {
  _classCallCheck(this, Test);
};

В примере классу es6 нужна вспомогательная функция _classCallCheck.Представьте, что если мы используем класс es6 в нескольких файлах, то в каждом файле нужно снова определить функцию _classCallCheck, что также является большим расточительством.Извлечение вспомогательных функций в package, на который ссылаются все файлы, может значительно сократить объем кода. а также@babel/runtimeОн делает именно это, он предоставляет различные вспомогательные функции, но как мы узнаем, какую вспомогательную функцию вводить? Вы не можете импортировать его вручную.@babel/plugin-transform-runtimeПлагины помогают нам автоматически вводить помощников. Сначала мы устанавливаем@babel/runtimeа также@babel/plugin-transform-runtime:

npm install @babel/runtime @babel/plugin-transform-runtime

Затем измените конфигурацию babel следующим образом:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "usage",
      "targets": "ie >= 8"
    }]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]  
}

Теперь посмотрим на файл test-compiled.js, вспомогательная функция _classCallCheck в нем уже из@babel/runtimeПредставлено:

/* test.js */
class Test {}


/* test-compiled.js */
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

Увидев это, вы можете сказать, что это не чепуха! Сколько объема могут мне сэкономить несколько вспомогательных функций, прежде чем я поленюсь устанавливать плагины. Фактически@babel/plugin-transform-runtimeЕсть еще одна важная функция, которая создаетsandboxed environment(среда песочницы), что особенно важно, когда вы пишете обычный код, например библиотеки классов.

Как упоминалось выше, для Promise, Map и других стандартных API es6+ мы предоставляем полифиллы для совместимости с браузерами более ранних версий. Это будет иметь побочный эффект в виде загрязнения глобальных переменных. Если вы пишете приложение, это нормально. Но если вы пишете общедоступную библиотеку классов, это может вызвать некоторые проблемы, ваша библиотека классов может перезаписать некоторые глобальные API. к счастью@babel/plugin-transform-runtimeпредоставляет нам элемент конфигурацииcorejs, который изолирует эти переменные в локальной области видимости:

/* .babelrc */

{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "targets": "ie >= 8"
    }]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 2
    }]
  ]  
}

Примечание. Вы должны настроить COREDJS здесь и установить его одновременно@babel/runtime-corejs2, без настройки@babel/plugin-transform-runtimeПо умолчанию помощники для этих полифилов не вводятся. На данном этапе значение corejs обычно указывается равным 2, что приблизительно можно понимать как да.@babel/runtimeверсия. Давайте теперь посмотрим, во что был преобразован test-compiled.js:

/* test.js */
class Test {}
new Promise(() => {})


/* test-compiled.js */
import _Promise from "@babel/runtime-corejs2/core-js/promise";
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

new _Promise(function () {});

Как мы и хотели, для полифилла Promise создана среда песочницы.

Наконец, добавим кое-что в test.js:

/*  test.js */
class Test {}
new Promise(() => {})

const b = [1, 2, 3].includes(1)


/* test-compiled.js */
import _Promise from "@babel/runtime-corejs2/core-js/promise";
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

new _Promise(function () {});
var b = [1, 2, 3].includes(1);

Его можно найти,includes方法并没有引入辅助函数,可这明明也是es6里面的api啊。 Это потому чтоincludesЭто метод экземпляра массива, который необходимо изменить, чтобы выполнить полифилл.Array, который загрязняет глобальную окружающую среду, поэтому@babel/plugin-transform-runtimeОн не может обрабатывать методы экземпляра этих спецификаций es6+.

tips

Вышеизложенное в основном представляет собой все содержание этой статьи и, наконец, краткое изложение и моменты, на которые следует обратить внимание:

  1. В этой статье не упоминаетсяpreset-stage, фактически, Babel@7 устарел от его использования.Если вам нужно использовать синтаксис, который все еще предлагается, пожалуйста, добавьте соответствующий плагин напрямую.
  2. Для обычных проектов вы можете напрямую использоватьpreset-envНастроить полифиллы
  3. Для проектов библиотеки классов рекомендуется использовать@babel/runtime, вам нужно обратить внимание на использование некоторых методов экземпляра
  4. Содержание этой статьи основано на babel@ 7. Если у вас возникнут проблемы в проекте, вы можете попробовать его обновить.babel-loaderверсия ...Быть добавленным

полный текст