Сегодня будет рассказ про фронтендерский зоопарк. Начну издалека. Если вы фронт, то вы знаете, что наш код читается многими браузерами. Вы также знаете, что разные браузеры реализуют разные части стандарта языка, а одинаковые части реализуют по-разному. Одно время такая разница в прочтении превращала разработку в ад. Но довольно быстро появились инструменты, “уравнивающие” ваш код таким образом, чтобы во всех браузерах он читался одинаково. Мы во ВКонтакте местами поддерживаем IE 11, поэтому для нас вопрос кроссбраузерности стоит остро. В сети можно найти бесконечное количество полифиллов для почти всего, что придумано в стандарте: Array.prototype.map, Object.keys, Map, Promise, etc. Полифилл — это кусочек рантайм-кода, который реализует недостающий функционал так, чтобы он во всех браузерах работал одинаково. Представим, что у нас есть такой код: И нам не повезло так, что приходится поддерживать пользователей на IE. Сейчас самая популярная библиотека полифиллов — это core-js. Чтобы использовать её, что называется, втупую, можно просто сделать импорт всего пакета в самом начале вашего модуля: core-js весит очень много, поэтому умнее будет импортнуть только то, что вы планируете использовать: Вроде прикольно. Но далеко от совершенства. Ваше приложение усложняется, команда растёт и в какой-то момент вы начинаете испытывать дискомфорт от того, что на ревью кто-то в очередной раз не заметил, что в код завезли новый модный метод массива, забыв дописать импорт полифилла. Итог — АНДЕЙФАЙНД ИЗ НОТ Э ФАНКШН. Machines must suffer (c) Омар Хайям Андрей Ситник Переложить заботу о стабильности кода на машины — отличная затея. Почти всегда. Почти. Babel — прекрасный инструмент. Он позволяет нам писать на современном стандарте языка, делая за нас все необходимые преобразования в ту версию, которую мы ему укажем. Даже не так. Мы можем сказать бабелю: “мы поддерживаем вот такой набор браузеров, список найдёшь в .browserslistrc, всё, давай!” Даже не так. Бабель сам посмотрит в этот файл, если он есть в корне проекта. Причём в первую очередь бабель решает не задачу поиска полифилла для всяких модных методов. Его основная фича — ТРАНСПИЛЯЦИЯ кода. Это когда вы пишете ваш любимый { ...user, last_name: 'Doe' }  (оператор, появившийся в es2015), а эта хрень работает в IE 11, который вышел в 2013. Как так получается? С помощью транспиляции. Всё, что нам нужно сделать — это поставить пару пакетиков: И создать пару конфигов: Всё. Допустим, наш файл выглядит так: При запуске npx babel script.js в консоли мы увидим следующее: То есть Babel понял, что в нашем списке поддерживаемых браузеров затесался отсталый, который не знает о таком операторе. Поэтому он его ТРАНСПИЛИРУЕТ. В транспилированном коде уже нет спреда, есть какой-то свой спред, собранный из говна из es5. Транспиляция — это преобразование одной синтаксической конструкции в другую на этапе билда кода. Полифилл — кусочек рантайм-кода, который докидывает недостающие методы, функции, конструкторы и т.д. С помощью полифилла нельзя сделать так, чтобы ?? завёлся в IE 11. Потому что это незнакомая синтаксическая конструкция, которую никаким рантайм-кодом не сделать интерпретируемой. Её можно только заменить на другую перед тем, как она попадёт в браузер. Этим и занимается Babel. Бабель состоит из плагинов и пресетов. Каждый плагин отвечает за преобразование конкретной конструкции языка. Есть, например, плагин для преобразования JSX в React.createElement. Или преобразователь любимого всеми ?? в обычный тернарник. Пресеты — это тупо набор плагинов, объединенных по какому-то признаку. Есть, например, пресет для реакта. Или для тайпскрипта. Но самый прикольный пресет — это тот, который мы установили. @babel/preset-env — это умный пресет, который подключает только те плагины, которые нужны, основываясь на браузерах, которые поддерживает конкретный проект. Собсно, для этого мы и положили файлик .browserslistrc рядом с .babelrc. Давайте для эксперимента уберем IE 11 из браузерлиста и снова запустим бабель. Смотрите, что получается: В коде пресета есть знание о том, что последний хром поддерживает спред оператор. Поэтому, раз мы хотим поддерживать только этот браузер, нет никакого смысла транспилировать этот код. Это знание получается из пакета caniuse-lite, который находится в зависимостях browserslist, который находится в зависимостях у @babel/preset-env. Примечание @rock: @babel/preset-env берёт данные не из caniuse-lite, а из собственной базы @babel/compat-data Круто? Я считаю, что круто. С использованием современных конструкций языка вроде разобрались. Бабель будет решать проблему несовместимости за нас. Но если мы напишем нечто такое: и запустим бабель, то мы увидим, что Object.values остался нетронутым: Хотя этот метод появился в es2015 и IE 11 его не поддерживает. То есть этот код в IE упадёт с ошибкой! Как же нам быть? В @babel/preset-env есть решение этой проблемы. Давайте чутка подпилим наш конфиг: Если переводить на русский, то написано следующее: “дорогой пресет энв, если ты в коде заметишь современные конструкции, которые можно заполифиллить, то так пожалуйста и сделай. Используй для этого core-js 3-й версии. Спасибо” Перевод вольный. Запускаем бабель: Что за дьявольщина, спросите вы? Я вчера задавался тем же вопросом. Но давайте успокоимся и хладнокровно посмотрим на то, что натворил бабель. Мы видим, что теперь для Object.values был импортирован полифилл из core-js. То есть этот код будет работать. Точнее так. Этот код будет работать, когда мы все эти реквайры соберем в один большой JS-файл. Об этом чуть позже. Но какого чёрта он добавил столько казалось бы лишних полифиллов? Например, полифилл для Object.keys. Это метод из стандарта es5. На сайте caniuse.com написано, что IE 11 его поддерживает. Пресет сошёл с ума? На самом деле нет. Дело в том, что core-js, который мы присобачили к пресет энву, имеет собственный мап, в котором чётко описано, с каких версий та и или иная фича поддержана в браузере. И прикол в том, что хоть Object.keys и работает в IE, с точки зрения создателей core-js, он работает там неправильно. Об этом мне поведал мейнтенер бабеля. То есть сам пресет энв берет инфу о поддержке той или иной конструкции из caniuse-lite, а core-js — из собственного мапа. Веселуха! Ща мы будем собирать этот код вебпаком и смотреть, как меняется размер итогового файла в зависимости от настроек бабеля и наших браузерных предпочтений. Ставим babel-loader: Конфиг вебпака as simple as possible: После минимизации такой код весит 64 байта. Запускаем npx webpack 134 bytes То есть вебпак сделал x2, обмазав код своим бойлерплейтом. Терпимо. 1.2 KiB (x10) Желание поддерживать IE 11 увеличивает размер бандла в 10 раз. Стерпим и это. 134 bytes. Логично, так как все перечисленные браузеры ни в каких полифиллах не нуждаются. 22.7 KiB (x170). Ну вы поняли. Самая мерзость в том, что даже дефолтные настройки браузерлиста генерят такой огромный кусок JS. Наш index.js (справа) весит 2 KB, а полифиллы для него — больше 20. И всё ради одного браузера. При использовании @babel/preset-env обязательно позаботьтесь о том, чтобы указать список интересующих лично вас браузеров. Иначе ваши пользователи будут скачивать огромную кучу скорее всего ненужного кода. В топку IE. Фронтенд разработка