Почему вы можете обойтись без Babel

Автор оригинала: David Else
  • Перевод

Для будущих студентов курса "JavaScript Developer. Basic" подготовили перевод материала.

Также приглашаем всех желающих на открытый вебинар
«Код как проект — настройка окружения для разработки на JavaScript». На вебинаре участники вместе с экспертом рассмотрят основные инструменты, которые помогают делать код лучше и чище — prettier, eslint, husky.


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

Ознакомившись с этой статьей, вы поймете:

  • как выяснить, какие браузеры действительно требуют дополнительной поддержки в каждом конкретном случае.

  • как использовать редактор Visual Studio Code, чтобы обойтись без Babel.

  • существует другая альтернатива программного обеспечения, чтобы сделать ту же работу быстрее.

Что такое Babel и какую проблему он решает?

Babel — это компилятор, который преобразует ваш современный JavaScript для запуска в старых браузерах. Он также может выполнять и другие задачи, такие как преобразование синтаксиса JSX, но это не единственный инструмент для этого.

По мере развития браузеров добавляются новые функции API и ECMAScript. Различные браузеры развиваются с разной скоростью и расставляют акценты в качестве приоритетных для разных задач. Это ставит нас перед непростым выбором: как мы можем их все поддерживать и при этом использовать современные функции? Некоторые из них будут несовместимы.

Обычное решение заключается в том, чтобы использовать новейшие возможности и трансформировать их в более старый код, который будет понятен браузеру. Транспилирование (Transpiling — сочетание двух слов: transforming — преобразование и compiling — компиляция) описывает специализированный тип компиляции. Она имеет различные значения в разных контекстах. В нашем случае также существуют две отдельные составляющие для переноса (транспилирования).

Разница между транспилированием (transpiling) и полифилингом (polyfilling)

Транспилирование (Transpiling) — это процесс преобразования синтаксиса нового языка, который старые браузеры не могут понять, в старый синтаксис, который они распознают.

Приведем пример переноса оператора let:

// the new syntax `let` was added in ECMAScript 2015 aka ES6
let x = 11;

// `let` transpiles to the old syntax `var` if your transpiler target was ES5
var x = 11;

Полифилинг (Polyfilling) — это процесс добавления недостающих методов, свойств или API к старым браузерам путем предоставления собственной версии недостающего родного кода.

Это можно рассматривать как дополнение недостающих элементов. Например, вот полифил (polyfill) для isNaN:

// check if the method `isNaN` exists on the standard built-in `Number` object
if (!Number.isNaN) {
  // if not we add our own version of the native method newer browsers provide
  Number.isNaN = function isNaN(x) {
    return x !== x;
  };
}

Наилучшим способом для получения полифилов является использование core-js.

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

Альтернатива №1: не поддерживать древние браузеры

Если бы пользователи просто обновили свои браузеры, мы могли бы избежать хлопот, связанных с транспилированием, и они могли бы наслаждаться усовершенствованными функциями и производительностью нового браузера. К сожалению, все не так просто.

Главный виновник — крупные корпорации, которым приходится поддерживать старое программное обеспечение. Классическим примером является Internet Explorer, который с самого начала был помехой для веб-разработок.

Тем не менее, за последние годы многое изменилось в лучшую сторону. Теперь большинство существующих браузеров актуальны, то есть они постоянно обновляются. Microsoft продвигает свой современный браузер Edge, который, что удобно, использует тот же движок V8, как и Chrome, что означает сокращение количества поддерживаемых движков на один.

Чтобы определить, нужно ли поддерживать определенный браузер, задайте себе следующие вопросы.

1. Какие браузеры в настоящее время используют Ваши клиенты?

Если у вас уже есть веб-сайт или приложение, которое обслуживает одну и ту же клиентскую базу, вы можете получить эту информацию из программы аналитики. Ниже приведены некоторые последние статистические данные с британского сайта звуковой инженерии, которым я управляю. Если бы это было JavaScript-приложение с теми же клиентами из той же категории, я бы предположил, что они будут использовать те же браузеры.

Если у вас не установлено аналитическое программное обеспечение, вы не будете знать, какие браузеры вам нужно поддерживать. Вы должны будете сделать обоснованное предположение. Если у вас есть корпоративные клиенты, гораздо больше шансов, что вам понадобится поддержка IE11 (Internet Explorer 11), чем если бы вы занимались маркетингом для фанатов web-literate (грамотное программирование) технологий.

Когда вы поддерживаете браузер, то принимаете на себя обязательства. Вы действительно хотите иметь лишние тесты в каждом выпуске, дополнительные инструменты для настройки и больше различных функций, чтобы это обслуживать?

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

2. Какие современные функции браузера вы хотите использовать?

Использование современных функций языка и API (Application Programming Interfaces) браузера делает написание кода проще, быстрее и интереснее. Это также делает ваш код более удобным в обслуживании.

Если вам нравиться писать ES5 (ECMAScript) и использовать XMLHttpRequest(), тогда определенно не нужен Babel, но может потребоваться какая-нибудь специальная процедура.

3. Какие современные функции браузера поддерживают браузеры ваших клиентов?

Эти данные доступны через Can I use (могу ли я использовать), но это напрасная трата времени на их поиск вручную. Теперь, когда вы знаете названия браузеров, которые вы хотите поддерживать, поиск совместимых функций может быть автоматизирован с помощью удивительного приложения Browserlist (подробнее об этом в следующем разделе).

Альтернатива № 2: Используйте eslint-plugin-compat

Вы можете вообще избежать всего процесса переноса (transpiling) и вместо этого позволить вашему редактору кода предупредить вас, если вы используете какие-либо функции, которые слишком сложны для браузеров ваших клиентов. Это самый простой вариант, потому что он:

  • исключает любую зависимость от транспилиров (transpilers). Возвращает вам практический контроль над рабочим кодом.

  • если имеется современная функция, без которой вы не можете жить, то ее можно использовать применив полифил (polyfill). 

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

Создать тест

Прежде чем мы сможем разобраться в плюсах и минусах, надо убедиться, что наши альтернативы Babel могут выполнять ту же самую основную работу. Давайте сделаем небольшой тест.

Ниже приведен современный код, который должна поддерживать наша целевая среда после переноса (transpiled).

После переноса (transportation) для каждой функции есть console.assert (метод записи сообщений на консоли для пользователя), чтобы убедиться, что она работает, как положено. В случае eslint-plugin-compat вместо этого проверим, что несовместимый код помечен в linting (Linting — это процесс, выполняемый программой linter, которая анализирует исходный код на определенном языке программирования и отмечает потенциальные проблемы, такие как синтаксические ошибки, отклонения от предписанного стиля кодирования или использование конструкций, о которых известно, что они небезопасны).

test.js

// test nullish coalescing - return right side when left side null or undefined
const x = null ?? "default string";
console.assert(x === "default string");

const y = 0 ?? 42;
console.assert(y === 0);

// test optional chaining - return undefined on non existent property or method
const adventurer = {
  name: "Alice",
  cat: {
    name: "Dinah",
  },
};

const dogName = adventurer.dog?.name;
console.assert(dogName === undefined);

console.assert(adventurer.someNonExistentMethod?.() === undefined);

// use browser API fetch, to check linting
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then((response) => response.json())
  .then((json) => console.log(json));

Использование eslint env свойства с помощью eslint-plugin-compat

Нам нужен обходной путь для объединения функций языка и API браузера.

Вы можете использовать eslint (Eslint – это утилита, проверяющая стандарты кодирования на JavaScript) для проверки синтаксиса языка. Для этого измените свойство env на es2020.

Для проверки совместимости API браузера используйте eslint-plugin-compat. Он использует ту же самую конфигурацию Browserlist и остальные инструменты, что и Babel.

Полную инструкцию можно найти в eslint-plugin-compat repo. Мы воспользуемся browserlist defaults как предустановками по умолчанию. Замените их по своему выбору, основанному на аналитике.

Что такое browserlist?

Browserlist автоматически выбирает список браузеров, основываясь на различных критериях, которые Вы ему предоставляете.

Посмотрите список браузеров, поддерживаемых defaults для browserlist

defaults — использует быстрый доступ к таким версиям браузеров:

  • > 0,5 процента (версии браузеров, выбранные по глобальной статистике использования)

  • Последние две версии (каждого "живого (not dead)" браузера) 

  • Firefox ESR (Extended Support Release)

  • “Живые (not dead)”  (браузеры без официальной поддержки и обновлений в течение 24 месяцев).

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

Настройка eslint-plugin-compat для Visual Studio Code

Добавьте следующие пакеты в свой проект.

npm install --save-dev eslint eslint-plugin-compat

Добавьте следующее в package.json.

"browserslist": [
    "defaults"
  ]

Создайте следующий файл .eslintrc.json или добавьте эти настройки к существующим.

{
  "extends": ["plugin:compat/recommended"],
  "env": {
    "browser": true,
    "es2020": true
  }
}

Убедитесь, что у вас установлено расширение VS Code ESLint.

Теперь любой API браузера, несовместимый с конфигурацией browserlist в вашем package.json, отображается как ошибка linting. Вы можете отдельно контролировать, какую версию ECMAScript вы хотите поддержать, используя свойство env в файле .eslintrc.json.

Было бы неплохо, если бы eslint-plugin-compat автоматически добавил и возможности языка, но на данный момент это является нерешённой задачей.

IE 11 с выбранной настройкой  —

— наш API fetch() помечен.

Поменяйте объект env на es6.

Вы сразу же увидите ошибку при попытке использовать nullish coalescing, который был запущен в составе Es2020.

Альтернатива № 3: Используйте другое программное обеспечение для замены Babel

Прежде чем рассматривать альтернативы, давайте быстро рассмотрим, как использовать Babel.

Использование Babel для транспилирования (transpile) и полифилинга (polyfill)

Сначала создайте директорию мини-проекта и установите нужные нам взаимосвязи.

mkdir babel-test
cd babel-test
npm init -y
mkdir src dist
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill

Добавьте следующее в свой package.json.

"browserslist": "defaults",

Запишите файл test.js  в src, а затем выполните следующую команду.

npx babel src --out-dir dist --presets=@babel/env

Наконец, запустите файл, чтобы проверить, что тесты все еще работают.

node dist/test.js

Ошибок ввода не должно быть, но будет сказано, что fetch is not defined, так как в Node.js нет метода fetch()

Вот результирующий транспилированный (transpiled) код. Обратите внимание на весь лишний мусор и хлам.

"use strict";

var _ref, _, _adventurer$dog, _adventurer$someNonEx;

// test nullish coalescing - return right side when left side null or undefined
var x = (_ref = null) !== null && _ref !== void 0 ? _ref : "default string";
console.assert(x === "default string");
var y = (_ = 0) !== null && _ !== void 0 ? _ : 42;
console.assert(y === 0); // test optional chaining - return undefined on non existent property or method

var adventurer = {
  name: "Alice",
  cat: {
    name: "Dinah",
  },
};
var dogName =
  (_adventurer$dog = adventurer.dog) === null || _adventurer$dog === void 0
    ? void 0
    : _adventurer$dog.name;
console.assert(dogName === undefined);
console.assert(
  ((_adventurer$someNonEx = adventurer.someNonExistentMethod) === null ||
  _adventurer$someNonEx === void 0
    ? void 0
    : _adventurer$someNonEx.call(adventurer)) === undefined,
); // use browser API fetch, to check linting

fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then(function (response) {
    return response.json();
  })
  .then(function (json) {
    return console.log(json);
  });

Преимущества и недостатки использования Babel

Преимущества:

  • Эта базовая установка была относительно несложной.

  • У Babel есть большое сообщество для поддержки и постоянных обновлений с 36.8k GitHub звездами на момент написания статьи.

Недостатки:

  • Медленное время компиляции

  • Множество зависимостей (dependencies), даже если они являются зависимостями (dev-dependencies). (установлено 269 пакетов)

  • 39М использованного дискового пространства, как сообщает du -sh

  • 5728 установленных файлов, о чем сообщает find . -тип f | wc -l

Использование swc для транспилирования (transpile) и полифилинга (polyfill)


Swc — новый конкурент Babel. Он написан на языке программирования Rust и в 20 раз быстрее. Это может быть очень важно, если вы долго ждете, чтобы построить свой проект.

Чтобы все устроить:

mkdir swc-test
cd swc-test
npm init -y
mkdir src dist
npm install --save-dev @swc/cli @swc/core browserslist

Добавьте следующее в свой package.json.

"browserslist": "defaults",

Запишите конфигурационный файл .swcrc  в корневую директорию проекта.

{
  "env": {
    "coreJs": 3
  },
  "jsc": {
    "parser": {
      "syntax": "ecmascript"
    }
  }
}

Запишите ваш тестовый файл в src, затем выполните следующую команду для переноса (transpile).

npx swc src -d dist

Запустите полученный файл, чтобы проверить, что тесты все еще работают.

node dist/test.js

В итоге swc-transpiled (транспилированный)  файл, выглядит вот так:

var ref, ref1;
var ref2;
// test nullish coalescing - return right side when left side null or undefined
var x = (ref2 = null) !== null && ref2 !== void 0 ? ref2 : "default string";
console.assert(x === "default string");
var ref3;
var y = (ref3 = 0) !== null && ref3 !== void 0 ? ref3 : 42;
console.assert(y === 0);
// test optional chaining - return undefined on non existent property or method
var adventurer = {
  name: "Alice",
  cat: {
    name: "Dinah",
  },
};
var dogName =
  (ref = adventurer.dog) === null || ref === void 0 ? void 0 : ref.name;
console.assert(dogName === undefined);
console.assert(
  ((ref1 = adventurer.someNonExistentMethod) === null || ref1 === void 0
    ? void 0
    : ref1.call(ref1)) === undefined,
);
// use browser API fetch, to check linting
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then(function (response) {
    return response.json();
  })
  .then(function (json) {
    return console.log(json);
  });

Преимущества и недостатки использования swc

Преимущества:

  • Гораздо меньше зависимостей (установлено 43 пакета)

Недостатки:

  • Меньшая пользовательская база и количество постоянных участников

Другие альтернативы: Google Closure Compiler и TypeScript

Я не включил Google Closure Compiler в качестве опции, потому что он, как это ни печально, сложен в использовании. Тем не менее, он может сделать хорошую работу по транспилированию (transpile) и полифилингу (polyfill). Если у вас есть свободное время, я рекомендую вам проверить его — особенно если вы цените небольшой размер файла, так как встроенная функция минификации демонстрирует отличные результаты.

Вы также можете использовать TypeScript для переноса (transpile) и core-js для ручной полифил (polyfill) обработки, но это неуклюжее решение, которое может с легкостью создать больше проблем, чем решить их.

Заключение

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

При необходимости Вы можете использовать функцию linting, чтобы убедиться в их совместимости друг с другом. Это избавит Вас от лишних хлопот, связанных с созданием специального сборочного процесса и транспиляции (transpilation).

Если вы выберете автоматический перевод, то SWC будет намного быстрее, чем Babel, и будет содержать гораздо меньше зависимостей. Также есть возможность использовать Google Closure Compiler или TypeScript, но для их настройки потребуется немного больше усилий.

LogRocket: Полная видимость ваших веб-приложений

LogRocket — это передовое решение для мониторинга приложений, позволяющее воспроизводить проблемы так, как если бы они возникали в вашем собственном браузере. Вместо того, чтобы гадать, почему происходят ошибки, или спрашивать пользователей на скриншотах и дампах логов, LogRocket позволяет воспроизводить сеанс, чтобы быстро понять, что пошло не так. Он отлично работает с любым приложением, независимо от фреймворка, и имеет плагины для записи дополнительного контекста из Redux, Vuex и @ngrx/store.

В дополнение к регистрации действий и состояния Redux, LogRocket записывает журналы консольных сообщений, ошибки JavaScript, следы стеков, сетевые запросы/ответы в формате заголовок + тело, метаданные браузера и пользовательские журналы. Кроме того при помощи DOM (Document Object Model) позволяет записывать страницы HTML и CSS, воссоздавая превосходные в пиксельном отношении видео даже для самых сложных одностраничных приложений.


Узнать подробнее о курсе "JavaScript Developer. Basic".

Смотреть открытый вебинар «Код как проект — настройка окружения для разработки на JavaScript».

OTUS
Цифровые навыки от ведущих экспертов

Комментарии 2

    +7

    Обожаю такие "альтернативы".


    Альтернатива №1: не поддерживать древние браузеры

    Как этого добиться, если вам таки их нужно поддерживать? Придти лично ко всем (или хотя бы большей части) пользователей, погрозить им пальчиком, и сказать "ай-яй-яй, а ну-ка закрыли этот ваш IE11"?
    И если еще в области модных хипстерских B2C-начинаний можно просто послать всех лесом красной плашкой "ваш браузер говно, вы ничего не понимаете в браузерах" поверх сайта, то в B2B такого подхода принципиально никто не поймет. В первую очередь ваш работодатель.
    Да и в области B2C с длинным историческим прошлым — у вас скорее всего есть какой-нибудь 1%, но живых платящих клиентов, сидящих на старом браузере, и никто вас по голове не погладит, если вы в один момент их пошлете в пешее эротическое.


    Риторические вопросы о цене поддержки старых браузеров в этом подпунктике — неуместны. Вы просто берете и втыкаете бабель, и внезапно весь ваш код работает. В программе-минимум — всё, больше ничего не требуется. Вместе с транспиляцией css вы даже сможете быть на 95% уверенными, что у вас будет всё работать, даже если в визуальной части могут быть расхождения.


    Альтернатива № 2: Используйте eslint-plugin-compat

    "А вы просто не пишите эти ваши зумерские стрелочные функции" (с). Давайте удорожать разработку вместо прогона бабеля? По-моему прекрасное решение. Да и разработчики обрадуются, им за те же фичи теперь будут три месячные зарплаты платить вместо одной.
    А главное, eslint-plugin-compat вам расскажет про то, что у вас сломается в css (нет), и что же вам делать, если из-за отсутствия фич вам придётся написать в пару раз больше кода (нет).


    Альтернатива № 3: Используйте другое программное обеспечение для замены Babel

    Тут я просто скажу так: за 8 лет веб-разработки с транспиляцией — случалось около четырех скользких моментов, когда мне приходилось лезть шаловливыми ручками внутрь бабеля (плагинов, конечно же), чтоб что-нибудь там срочно пропатчить, пока не выйдет новая версия.
    Если б это всё было на расте — то конечно же у меня откуда-то бы нашлось время изучить раст (нет).


    Итого — бабель на текущий исторический период это именно таки "вынужденная необходимость", наименьшее зло из возможных. Если только ваши начальные условия не настолько экзотичны, чтоб иметь возможность его не использовать вообще.

      0

      Конечно предложенные 3 альтернативы довольно специфичны. Вряд ли разумно на данный момент в серьезных проектах бросать поддержку не очень свежих браузеров, если эта поддержка цена этой поддержки только добавление Babel. Альтернатива 2 — вообще очень странная, а альтернатива 3 пока рискованная.
      Но все же хочу сказать спасибо за статью, важно знать что альтернативы есть. SWC — библиотека, за развитием которой стоит последить.
      Сам стараюсь использовать TypeScript, а Babel, когда нельзя отказаться от JS.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое