Стандарт Ecmascrpit
Стандарт Ecmascrpit

В 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 ?

Комментарий

Browserlist

Нет

Не является инструментом сборки, но используется многими инструментами сборки внутри и является самым популярным инструментом с открытым исходным кодом для настройки целевых браузеров поддержки. Настройка по умолчанию больше не включает браузеры ES5. Последним был IE 11, который был отмечен как мертвый в версии 4.21.

Babel

Да

В документации Babel рекомендуется задать параметр target (который использует Browserlist), но если ничего не указано, весь код будет транспилирован в ES5.

Webpack

Нет

По умолчанию webpack не транспилирует никакой код. Большинство пользователей webpack включают babel-loader, и пример использования webpack для этого предлагает установить цели: "defaults".

Typescript (tsc)

Да

По умолчанию в конфиге TypeScript target - ES5.

Next.js

Нет

Next.js использует Babel для транспиляции и по умолчанию устанавливает конфигурацию Browserlist, ориентированную на «современные браузеры» (т. е. браузеры, поддерживающие модули ES).

esbuild

Нет

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

Vite

Нет

Vite использует esbuild и по умолчанию устанавливает кастомную цель для «современных браузеров» (т. е. браузеров, поддерживающих модули ES). Vite позволяет пользователям устанавливать плагин, если им нужна поддержка устаревших браузеров.

Rollup

Нет

Rollup не транспилируется по умолчанию. Многие пользователи Rollup устанавливают @rollup/plugin-babel, в этом случае используются значения Babel по умолчанию.

Parcel

Нет

Parcel автоматически применяет дифференцированную подачу с настраиваемыми целями.

Closure Compiler

Нет

По умолчанию используется ECMASCRIPT_NEXT, представляющий собой последний набор стабильных функций ES.

Как показывает эта таблица, подавляющее большинство сборщиков и инструментов сборки больше не транспилируют в ES5 по умолчанию. Также примечательно, что новые инструменты вообще не поддерживают ES5, что показывает, что тенденция движется в этом направлении.

Тем не менее, Babel по-прежнему является самым популярным инструментом для транспилирования JavaScript, и в результате транспилирование в ES5 по-прежнему довольно распространено в Интернете (см. ниже использование ES5 в продакшене для получения более подробной информации).

Популярные Javascript-библиотеки

В дополнение к рассмотрению популярных инструментов сборки я также рассмотрел некоторые из самых популярных библиотек, используемых сегодня (опять же на основе опроса State of JS, в приблизительном порядке популярности):

Чтобы протестировать каждую из этих библиотек, я создал точку входа в пакет, которая импортировала только эту конкретную библиотеку, используя один из примеров кода из документации библиотеки. Затем я объединил код с помощью Rollup и Webpack, чтобы протестировать вывод и посмотреть, включает ли он какой-либо синтаксис ES6+ (в частности, какой-либо синтаксис ES6+, который не поддерживается в IE 11).

Вот что я нашел:

Библиотека

Содержит ES6+ синтаксис?

Комментарии (оставил оригинальные термины)

Lodash

Нет

только ES5

React

Нет

только ES5

date-fns

Да

arrow functions

three.js

Да

async/await, arrow functions, spread, destructuring

d3

Да

arrow functions, spread, destructuring

Framer-motion

Да

arrow functions, spread, destructuring

greensock

Нет

только ES5

dayjs

Нет

только ES5

Zod

Да

async/await, arrow functions, spread, destructuring

RxJS

Да

arrow functions

immer

Да

arrow functions, spread, destructuring

luxon

Да

async/await, arrow functions, spread, destructuring

react-query

Нет

только ES5

Как показывают результаты выше, многие популярные библиотеки JavaScript теперь публикуют синтаксис ES6+.

Это примечательно, поскольку, как я упоминал ранее, большинство разработчиков, использующих Babel для транспиляции своих исходных файлов при сборке, явно настраивают свой сборщик так, чтобы он не транспилировал ничего в каталоге node_modules — что было основной причиной, по которой авторы библиотек исторически считали, что им нужно продолжать транспилировать в ES5.

Например, на момент публикации этой статьи (сентябрь 2024 г.):

А 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-анимация

Комментарии (1)


  1. Spaceoddity
    15.09.2024 13:09

    Я писал относительно недавно проект с поддержкой ES5 - тот ещё геморрой... Все уже настолько привыкли к "сущностям" ES6+, что время разработки увеличивается кратно! По дефолту практически в каждой функции вылезает какая-нибудь "красная бяка", все наработанные паттерны в топку! Гугл и справочные ресурсы постоянно подсовывают рецепты ES6+, надо каждую строчку кода перепроверять с лупой - программирование микроконтроллеров какое-то просто получается))

    Плюс всё это размывается браузерной поддержкой - вроде конструкция из более позднего стандарта, но целевой браузер её "прожёвывает"...