В 2017 году я написал статью, в которой показал веб-разработчикам, как они могут деплоить код ES6+ (он же ES2015) в продакшен, без необходимости транспилировать его в ES5. Этот метод был выходом для разработчиков веб-сайтов, которые хотели без ограничений писать современный код, не беспокоясь о раздувании транспилера или полифилла.
К сожалению, хотя многие разработчики веб-сайтов могли использовать этот метод, большинство авторов библиотек JavaScript не могли.
Авторы библиотек сталкиваются с гораздо большими ограничениями, чем разработчики веб-сайтов, поскольку они не контролируют, как развертывается их код. Кроме того, поскольку многие из популярных инструментов сборки рекомендуют разработчикам исключить директорию node_modules из транспиляций, авторы библиотек были вынуждены быть крайне консервативными — обычно транспилируя все обратно в ES5, чтобы избежать потенциальной поломки сайтов.
Но это было семь лет назад, и с тех пор в области инструментов JavaScript произошло множество улучшений. Ландшафт браузеров также сильно изменился. В частности, IE 11, последний оставшийся браузер ES5, перестал поддерживаться Microsoft в 2022 году, что означало, что многие компании могут наконец прекратить его поддержку.
Так каково текущее состояние ES5 в Интернете сегодня? И каковы наилучшие практики для веб-разработчиков при создании кода прода?
В этой статье рассматриваются данные, которые у нас есть, чтобы ответить на эти вопросы. В ней также предлагаются некоторые конкретные рекомендации (основанные на этих данных) о том, как и разработчики веб-сайтов, и авторы библиотек должны подходить к поддержке устаревших браузеров в будущем.
Небольшой дисклеймер
Прежде чем углубляться в реальные данные об использовании ES5, я хочу прояснить, что нет ничего изначально неправильного в создании или публикации кода ES5.
Движки JavaScript оптимизируют код ES5 гораздо дольше, чем современный код, поэтому если у вас есть старый код ES5, который все еще работает, нет смысла обновлять его только для того, чтобы сделать его «современным».
Однако, если вы создаете код в синтаксисе ES6+, а затем используете инструмент сборки для его транспиляции в ES5, это обычно приводит к большому количеству полифиллов и транспиляторов, что может значительно увеличить размер ваших конечных бандлов.
Чтобы проиллюстрировать этот момент, приведем пример:
console.log([1, 2, 3].at(-1));
Если вручную транслировать этот код в ES5, он, вероятно, будет выглядеть примерно так:
var arr = [1, 2, 3];
console.log(arr[arr.length - 1]);
Однако если вы транспилируете эту единственную строку кода с помощью Babel и настроите его для добавления полифиллов — даже если вы ограничили его только необходимыми полифиллами на основе использования в исходном коде — он включает 71 зависимость core-js и уменьшается с 31 байта до 11 217 байт в минифицированном виде!
Цель этого примера не в том, чтобы пристыдить Babel или core-js. Эти инструменты должны поддерживать весь возможный код ES6+, что требует от них учета всевозможных пограничных случаев (хотя в этом конкретном примере их нет).
Вместо этого цель состоит в том, чтобы подчеркнуть, что выбор поддержки устаревших браузеров имеет свою цену, и эта цена может быть значительной.
К сожалению, проблема на самом деле хуже, чем просто раздувание кода. Если посмотреть на приведенные ниже данные о том, как популярные сегодня веб-сайты фактически транспилируют и деплоят свой код в продакшен, то окажется, что большинство сайтов в Интернете поставляют код, транспилированный в ES5, но все равно не работающий в IE 11. Это означает, что транспилер и раздутый полифил загружаются 100% их пользователей, но не приносят пользы никому из них.
Данные
Чтобы понять состояние ES5 в Интернете, вам нужно рассмотреть три вещи, поскольку все они играют важную роль в финальном виде нашего кода, который мы, как пользователи Интернета, получаем:
-
Конфигурации по умолчанию популярных сборщиков и инструментов сборки
-
Состояние кода, обнаруженного в популярных библиотеках JavaScript
-
Состояние кода, развернутого владельцами веб-сайтов
Дефолтные настройки сборщиков
Большинство сборщиков и инструментов сборки очень гибкие и предлагают практически неограниченный контроль над конечным результатом. Однако на практике большинство разработчиков просто используют значения по умолчанию, поэтому значения по умолчанию имеют большое значение.
Что это за значения по умолчанию? В частности, приводят ли значения по умолчанию к транспиляции кода в ES5?
Чтобы ответить на этот вопрос, я взглянул на выходные данные, сгенерированные некоторыми из самых популярных инструментов сборки, согласно последнему исследованию State of JS (2023), упорядоченные примерно по популярности:
Инструмент |
По дефолту транспилирует в ES5 ? |
Комментарий |
Нет |
Не является инструментом сборки, но используется многими инструментами сборки внутри и является самым популярным инструментом с открытым исходным кодом для настройки целевых браузеров поддержки. Настройка по умолчанию больше не включает браузеры ES5. Последним был IE 11, который был отмечен как мертвый в версии 4.21. |
|
Да |
В документации Babel рекомендуется задать параметр target (который использует Browserlist), но если ничего не указано, весь код будет транспилирован в ES5. |
|
Нет |
По умолчанию webpack не транспилирует никакой код. Большинство пользователей webpack включают babel-loader, и пример использования webpack для этого предлагает установить цели: "defaults". |
|
Да |
По умолчанию в конфиге TypeScript target - ES5. |
|
Нет |
Next.js использует Babel для транспиляции и по умолчанию устанавливает конфигурацию Browserlist, ориентированную на «современные браузеры» (т. е. браузеры, поддерживающие модули ES). |
|
Нет |
esbuild не транспилирует по умолчанию. Вы можете задать кастомную цель для включения транспилирования, но ES5 не поддерживается как цель транспилирования. |
|
Нет |
Vite использует esbuild и по умолчанию устанавливает кастомную цель для «современных браузеров» (т. е. браузеров, поддерживающих модули ES). Vite позволяет пользователям устанавливать плагин, если им нужна поддержка устаревших браузеров. |
|
Нет |
Rollup не транспилируется по умолчанию. Многие пользователи Rollup устанавливают @rollup/plugin-babel, в этом случае используются значения Babel по умолчанию. |
|
Нет |
Parcel автоматически применяет дифференцированную подачу с настраиваемыми целями. |
|
Нет |
По умолчанию используется ECMASCRIPT_NEXT, представляющий собой последний набор стабильных функций ES. |
Как показывает эта таблица, подавляющее большинство сборщиков и инструментов сборки больше не транспилируют в ES5 по умолчанию. Также примечательно, что новые инструменты вообще не поддерживают ES5, что показывает, что тенденция движется в этом направлении.
Тем не менее, Babel по-прежнему является самым популярным инструментом для транспилирования JavaScript, и в результате транспилирование в ES5 по-прежнему довольно распространено в Интернете (см. ниже использование ES5 в продакшене для получения более подробной информации).
Популярные Javascript-библиотеки
В дополнение к рассмотрению популярных инструментов сборки я также рассмотрел некоторые из самых популярных библиотек, используемых сегодня (опять же на основе опроса State of JS, в приблизительном порядке популярности):
Чтобы протестировать каждую из этих библиотек, я создал точку входа в пакет, которая импортировала только эту конкретную библиотеку, используя один из примеров кода из документации библиотеки. Затем я объединил код с помощью Rollup и Webpack, чтобы протестировать вывод и посмотреть, включает ли он какой-либо синтаксис ES6+ (в частности, какой-либо синтаксис ES6+, который не поддерживается в IE 11).
Вот что я нашел:
Библиотека |
Содержит ES6+ синтаксис? |
Комментарии (оставил оригинальные термины) |
---|---|---|
Нет |
только ES5 |
|
Нет |
только ES5 |
|
Да |
arrow functions |
|
Да |
async/await, arrow functions, spread, destructuring |
|
Да |
arrow functions, spread, destructuring |
|
Да |
arrow functions, spread, destructuring |
|
Нет |
только ES5 |
|
Нет |
только ES5 |
|
Да |
async/await, arrow functions, spread, destructuring |
|
Да |
arrow functions |
|
Да |
arrow functions, spread, destructuring |
|
Да |
async/await, arrow functions, spread, destructuring |
|
Нет |
только ES5 |
Как показывают результаты выше, многие популярные библиотеки JavaScript теперь публикуют синтаксис ES6+.
Это примечательно, поскольку, как я упоминал ранее, большинство разработчиков, использующих Babel для транспиляции своих исходных файлов при сборке, явно настраивают свой сборщик так, чтобы он не транспилировал ничего в каталоге node_modules — что было основной причиной, по которой авторы библиотек исторически считали, что им нужно продолжать транспилировать в ES5.
Например, на момент публикации этой статьи (сентябрь 2024 г.):
-
Документация по babel-loader Webpack рекомендует конфигурацию, исключающую node_modules.
-
Документация по plugin-babel Rollup рекомендует исключить node_modules, а также рекомендует авторам библиотек не публиковать код ES6.
А TypeScript (tsc), второй по популярности инструмент транспиляции после Babel, будет транспилировать только собственные файлы кода проекта. Он не будет транспилировать зависимости проекта в node_modules.
Это создает проблему для любого веб-сайта, который хочет поддерживать ES5 и использует Babel или tsc для транспилирования своего кода. Если у них нет глубокого понимания того, как все части их пайплайна сборки взаимодействуют друг с другом, и если они не знают, как правильно настроить каждый из них, они, скорее всего, объединяют код ES6+ со своим кодом ES5, не осознавая этого.
Таким образом, возникает вопрос, действительно ли это вызывает проблему для реальных веб-сайтов или большинство из них правильно настраивают свои инструменты? В следующем разделе рассматриваются данные из HTTP-архива, чтобы ответить на этот вопрос.
Примечание: некоторые библиотеки в таблице выше публикуют как версии ES5, так и ES6+, обычно с версией ES5, установленной в поле package.main, и версией ES6+, установленной либо в поле package.module, либо в поле package.exports. В этих случаях я смотрел только на версию скрипта, которую получал бандлер при использовании конфигурации по умолчанию (так как большинство людей используют именно ее), а бандлеры сегодня по умолчанию используют package.module или package.exports вместо package.main (см.: [1], [2], [3]).
Использование ES5 в проде
Три основных инструмента, которые разработчики используют для транспиляции кода ES6+ в ES5:
-
Babel
-
TypeScript (tsc)
-
Closure Compiler (JSCompiler)
Все три этих инструмента включают в себя некоторые формы полифиллов и так называемые «вспомогательные» функции ES5, чтобы избежать дублирования в результате. Наиболее распространенные библиотеки вспомогательных функций ES5, используемые этими инструментами: babel-helpers, core-js, regenerator-runtime, tslib и $jscomp.
Многие функции в этих вспомогательных библиотеках достаточно уникальны, чтобы можно было обнаружить (даже в минифицированном коде), какие сайты их используют, запросив HTTP-архив. Поиск наличия этих вспомогательных функций — а не стандартного синтаксиса ES5 (например, var или нестрелочная функция) — помогает отличить старый код ES5, написанный вручную (обычно довольно оптимизированный), от нового кода ES5, сгенерированного транспилером (обычно довольно раздутого).
Я провел поиск в HTTP Archive, чтобы узнать, насколько распространено включение этих помощников в пакеты скриптов, которые они развертывают в производстве, на популярных веб-сайтах (топ-10 000, согласно рейтингу популярности CrUX). Я также хотел узнать, насколько распространено использование сайтами нетранспилированного синтаксиса ES6+.
Вот что я нашел (полные результаты):
-
89% сайтов обслуживают как минимум 1 файл JavaScript, содержащий нетранспилированный синтаксис ES6+.
-
79% сайтов обслуживают как минимум 1 файл JavaScript, содержащий вспомогательный код ES5.
-
68% сайтов обслуживают как минимум 1 файл JavaScript, содержащий как вспомогательный код ES5, так и нетранспилированный синтаксис ES6+ в одном файле.
Последнее открытие просто взорвало мне
Повторю то, что я сказал ранее — потому что это заслуживает повторения — если браузер не поддерживает синтаксис ES6+ (например, IE 11), то он выдаст ошибку при попытке загрузить файл скрипта, содержащий синтаксис ES6+. А если браузер поддерживает синтаксис ES6+, то ему не нужен ни вспомогательный код ES5, ни устаревшие полифиллы. Нет абсолютно никаких причин включать и то, и другое.
Чтобы еще раз проверить точность результатов этого запроса, я вручную протестировал 20 случайных сайтов из списка и подтвердил, что они действительно включают как вспомогательный код ES5, так и синтаксис ES6+ в некоторые из тех же пакетов скриптов. Я также вручную посетил эти сайты в IE 11 и подтвердил, что эти бандлы скриптов действительно не загружаются.
Помните, что это не просто случайные сайты в Интернете. Это 10 000 самых популярных веб-сайтов в мире, на которые приходится подавляющее большинство всего использования веб-сайтов в мире.
Что это все значит?
Для сайта, предоставляющего пользователям код, содержащий как вспомогательные функции ES5, так и нетранспилированный синтаксис ES6+, есть только два правдоподобных объяснения:
-
Сайту не обязательно поддерживать браузеры ES5, но некоторые из их зависимостей транспилируются в ES5, поэтому в их выводе отображается код ES5.
-
Сайт планировал поддерживать браузеры ES5, но разработчики не учли, что некоторые из их зависимостей публикуют нетранспилированный синтаксис ES6+, и не настроили свой сборщик для транспилирования кода в node_modules.
Независимо от объяснения, тот факт, что так много самых популярных в мире веб-сайтов выдают так много ненужного кода, является сильным индикатором того, что настройки по умолчанию, которые в настоящее время рекомендуют наши инструменты, не работают.
Если в этих данных есть что-то хорошее, так это то, что мне совершенно ясно, что прекращение поддержки IE не окажет заметного влияния на большинство компаний. Если все эти крупные компании, по-видимому, не затронуты этими неисправными возможностями IE, то и ваши, вероятно, не затронут.
Рекомендации
Для авторов библиотек
Первоначальное обоснование того, почему авторы библиотек должны транспилировать в ES5, состояло в том, что большинству сайтов в любом случае нужно было транспилировать в ES5. Однако, учитывая, что 89% из 10 000 лучших веб-сайтов в настоящее время поставляют некоторый нетранспилированный синтаксис ES6+, это обоснование больше недействительно.
Учитывая данные, представленные в этой статье, авторам библиотек JavaScript определенно не имеет смысла транспилировать свой код в ES5.
С практической точки зрения, авторы библиотек не имеют информации о потребностях поддержки браузеров веб-сайтов, импортирующих их, поэтому им не имеет смысла принимать это решение для всех потребителей своей библиотеки. В то же время авторы библиотек не должны предполагать, что все потребители их библиотеки смогут запустить ее через сложный процесс сборки, поэтому важно, чтобы их опубликованный код использовал полностью стандартный JavaScript и работал в текущем наборе широко используемых браузеров.
Так какие же цели должны выбрать авторы библиотек? По моему мнению, лучшим решением для авторов библиотек является использование Baseline, а именно включение только функций Baseline Widely Available в любой опубликованный код.
Если вы не знакомы с Baseline, это попытка группы сообщества WebDX в W3C помочь разработчикам легко идентифицировать функции, которые стабильны и хорошо поддерживаются всеми основными браузерами и движками рендеринга браузеров на настольных компьютерах и мобильных устройствах. Функция считается Baseline Widely Available, если она доступна в стабильных версиях всех четырех основных браузеров в течение как минимум 30 месяцев.
Главное преимущество таргетинга на что-то вроде Baseline Widely Available заключается в том, что это движущаяся цель, то есть она не застрянет в прошлом, как это произошло с таргетингом на ES5 (и что в настоящее время происходит с целью esmodule, используемой Next.js, Vite и Parcel).
Авторы библиотек теперь могут настроить свою систему сборки для использования базовых широко доступных функций с помощью следующего запроса Browserlist (для любого инструмента, поддерживающего Browserlist):
targets: [
'chrome >0 and last 2.5 years',
'edge >0 and last 2.5 years',
'safari >0 and last 2.5 years',
'firefox >0 and last 2.5 years',
'and_chr >0 and last 2.5 years',
'and_ff >0 and last 2.5 years',
'ios >0 and last 2.5 years',
]
Примечание: открыт запрос на добавление поддержки Baseline в Browserlist, что упростит приведенный выше запрос до простого «baseline широко доступна».
Если сайту необходимо поддерживать больше браузеров, чем те, которые охвачены Baseline Widely Available, это на 100% нормально. Этот сайт всегда может настроить свою систему сборки для дальнейшей транспиляции любых импортируемых библиотек. Суть в том, что это решение лучше всего принимать разработчикам сайта, а не автору библиотеки.
Для Веб-разработчиков
Тот факт, что так много популярных веб-сайтов поставляют как нетранспилированный синтаксис ES6+, так и помощники ES5 в одном и том же пакете скриптов, является явным признаком того, что практика исключения каталога node_modules из транспиляций не является хорошей практикой.
Я утверждал, что это нехорошая практика с 2017 года, но большинство разработчиков, с которыми я общался, не хотели следовать этому совету, потому что это замедлило бы их сборки.
Однако в наши дни инструменты сборки стали значительно быстрее. Кроме того, сайты могут настраивать свои сборки для обработки только кода в node_modules при сборке для продакшена. В процессе разработки код должен нормально работать в любом браузере, который использует разработчик, особенно если авторы библиотек следуют совету, который я дал выше, и ориентируются на Baseline Widely Available.
Выводы
Как я уже упоминал выше, цель этой статьи не в том, чтобы обвинять или стыдить веб-сайты или авторов инструментов на основе этих результатов. Я также хочу убедиться, что ясно, что я определенно не предлагаю, чтобы веб-сайты все еще поддерживали IE 11. Если уж на то пошло, эти результаты говорят о том, что поддержка IE 11 не является необходимостью для большинства компаний, даже крупных с глобальной клиентской базой.
Я хочу, чтобы читатели извлекли из этой статьи следующие основные выводы:
-
ES5 больше не является тем, на что инструменты сборки или библиотеки JavaScript должны ориентироваться по умолчанию.
Если инструменты все еще хотят предлагать поддержку ES5, это должно быть чем-то, что отдельные сайты с особыми потребностями в поддержке могут выбрать. -
Инструменты сборки и библиотеки не должны использовать фиксированную политику поддержки браузера.
Эти политики могут быстро устареть, что приводит к сценарию, описанному в данных этой статьи. Решения о поддержке браузера должны приниматься самим сайтом, а не инструментами, которые он использует. Хорошая политика поддержки браузера для инструментов и библиотек — Baseline Widely Available. -
Разработчики веб-сайтов, которые импортируют сторонние библиотеки, должны обрабатывать эти библиотеки как часть своей сборки.
Не стоит полагать, что у всех авторов библиотек те же потребности в поддержке браузеров, что и у вас. И как показывают данные в этой статье, во многих случаях у разработчиков веб-сайтов могут быть более широкие потребности в поддержке браузеров, чем у импортируемых ими библиотек (и, следовательно, им необходимо дополнительно транспилировать их). -
Кроссбраузерная поддержка — это не то, на что вы должны опираться исключительно при сборке своего инструмента.
Если вам нужна поддержка определенного набора браузеров, вам нужно протестировать свой сайт, чтобы убедиться, что он работает в этих браузерах.
Надеюсь, эта статья была полезна всем, кто все еще беспокоится о поддержке ES5, или всем, кто пытается убедить других не беспокоиться!
Если вам интересно, обслуживает ли ваш сайт пакеты скриптов, которые содержат устаревшие помощники ES5, смешанные с нетранспилированным кодом ES6+, вы можете поискать свой сайт в данных, которыми я поделился выше, чтобы убедиться в этом самостоятельно.
И если вы обнаружите, что ваш сайт делает это, и сможете исправить это на основе рекомендаций из этой статьи, я буду рад услышать от вас!
Если вам понравилась статья, больше похожего материала и новостей из мира frontend-разработки в моем телеграм-канале Плавный, как css-анимация
Автор: andrrK