Избушка на обратно-совместимых ножках — компилируем JS для нужных браузеров
Привет, хабр! Уже сегодня на otus.ru стартует курс «Fullstack разработчик JavaScript» и я решил поддержать ребят этой статьей. Я кстати сам преподаю на курсе по React.js.
Есть такой принцип — Don’t break the web, который можно раскрыть как «веб всегда старается сохранить максимальную обратную совместимость». В некоторой мере этот принцип применим и к веб сайтам и приложениям — ваш сайт должен работать не только в одном конкретном браузере, но в целом наборе разных браузеров и версий. Но в каких? Однозначно должны быть какие-то разумные пределы и IE 6 и netscape navigator поддерживать не стоит, но два вопроса остаются открытыми: какие браузеры вы поддерживаете и как это обеспечить?
Если есть обратная совместимость значит что-то меняется. Меняются в вебе три вещи: ECMAScript (javascript), CSS и различные Web API. CSS мы сегодня оставим на опушке, а пока, тропинка ведет нас в дебри современной фронтенд разработки
Так или иначе, абсолютное большинство современных и не очень браузеров поддерживают ES5, этакий greatest common denominator. Большинство библиотек пишутся или скомпилированы в ES5 и мы может поступить также! Можно либо сразу писать в ES5 (не рекомендую) либо использовать babel или typescript
В сети есть множество туториалов о том как это делать, но позволю себе упомянуть что в babel 7, @babel/preset-es2015
является устаревшим (о чем нам любезно напоминает официальная документация) и рекомендуется использовать @babel/preset-env
, которому на самом деле посвящена львиная доля статьи. Но легко поставить избушку на поляне, но мы ведь с вами не за этим здесь собрались, давайте попробуем сделать это на болоте (если почувствовали тоску, то возможно стоит поменять работу)?
Общая идея следующая — мы определяем какие браузеры мы поддерживаем и под эти браузеры настраиваем нашу конфигурацию транспиляции. Помогут нам в этом как всегда различные инструменты, но проблема тут в том, что их не два, не три, а полное лукошко! Поэтому я сначала кратко объясню что каждый из них делает, а с тем кто с кем и как взаимодействует разберемся потом:
Дисклеймер: для интеграции @babel/preset-env
со стандартной конфигурацией в новый проект не требует понимания того, как эта машинерия работает, но в продакшене все как обычно сложнее и это начинается играть роль
caniuse
https://github.com/fyrd/caniuse
https://caniuse.com/
Также существует в редакции caniuse-lite (именно ее использует browserslist)
Наверное самый известный сайт на который разработчики ходят чтобы узнать в каких браузерах поддерживается необходимая фича. Отдельная его крутость состоит в том, что у них есть данные по использованию браузеров (в том числе и с разбивкой по странам)
browserslist
https://github.com/browserslist/browserslist
С него все начинается. browserslist
(browserSlist обратите внимание на S) умеет преобразовывать запрос вида »> 0.25%, not IE11, not dead» в список браузеров, в данном случае означающий все браузеры которые имеют мировую долю использования более 0.25% кроме IE11 и браузеров не получающих обновления безопасности (на момент написания статьи IE 10, IE Mobile 11, BlackBerry 10, BlackBerry 7, Samsung Internet 4 и Opera Mobile 12.1)
corejs
Содержит полифиллы для всех фич ECMAScript вплоть до 2019-й версии. В версии 3 произошло множество глобальных изменений (https://github.com/zloirock/core-js/blob/master/docs/2019–03–19-core-js-3-babel-and-a-look-into-the-future.md)
Используется в babel/present-env, поддерживается и 2-я и 3 -я версии
core-js-compat
https://www.npmjs.com/package/core-js-compat
Содержит информацию о том каким браузерам какие полифиллы из corejs нужны. В том числе подключает полифиллы для тех браузеров в которых есть критические баги в фиче. Например до Chrome 80 matchAll
не бросал исключение в случае неглобальной регулярки, как нужно по текущему стандарту, хотя имплементация впервые появилась в 73-й версии
https://github.com/zloirock/core-js/blob/master/packages/core-js-compat/src/data.js#L877
compat-table
https://kangax.github.io/compat-table/es6/
Содержит информацию о поддержке фич ECMAScript различными браузерами (думаю понятно) и средами исполнения (nodejs, graalvm etc.)
Используется в @babel/preset-env
, но с нюансом — в случае corejs@2
для полифиллов используются данные из compat-table
, а в случае corejs@3
— данные из core-js-compat
(они более актуальные и умные)
На практике в классическом варианте все выглядит так:
- в
.browserslistrc
(или вpackage.json#browserslist
) вы указываете browserslist query - в
babelrc
вы указываете@babel/preset-env
в presets и выбираетеопцию useBuiltIns
(советуюentry
ибоusage
не умеет проверять используемые API в зависимостях) - Эту browserslist query читает
@babel/preset-env
@babel/preset-env
получает из нее список браузеров через api browserslist и- использует
compat-data
выбирая набор плагинов которые будут применены в вашему коду - использует corejs-compat выбирая какие полифиллы подключить
- использует
Таким образом на выходе вы получаете js код который будет верно работать во всех указанных браузерах (в разумных пределах ибо есть разные экзотические браузеры про поддержку которых данных просто нет). И наша избушка встает на ножки (или на сваи) и получает обратную совместимость!
К сожалению нет, во фронтенде все опять неспокойно. Есть ряд кочек о которые можно легко споткнуться, а то и вообще завязнуть в трясине если сойти с узкой тропинки happy path
browserslist query
Наверняка у проекта в который вы собираетесь это внедрять уже список или хотя бы понимание поддерживаемых браузеров. Если нет, то все просто: возьмите defaults
(это > 0.5%, last 2 versions, Firefox ESR, not dead
) и вы покроете более 90% вообще всех браузеров (что на самом деле очень хороший результат ибо «все» это вплоть до IE 6!). Если все-таки понимание есть, но надо перевести его (понимание) в query. Это может быть не так-то просто, но главным образом нужно знать три вещи:
- запятая в browserslist query означает
or
(то есть «или») - «и» записывается как
and
- отрицание записывается как
not
и при этом применяется черезand
даже если написано с запятой (если кто-то помнит как заносить отрицание в скобки в булевой алгебре поймет почему) - ладно, четыре: можно удобно тестировать вашу query вот тут https://browserl.ist/ (а тут нет S! Жизнь боль)
С этим знанием и внимательным чтением документации (а вы думали можно без этого?) вы сможете подогнать список под ваши нужды. Если будут проблемы, дайте знать в комментариях!
Невиданное-неслыханное
Как сказано выше, defaults
это > 0.5%, last 2 versions, Firefox ESR, not dead
. Вроде относительно понятно, но если вы посмотрите чему это соответствует то обнаружите там браузеры о существовании которых и не подозревали или не знали что они до сих пор используются. Тем не менее слепо выбрасывать их не стоит, поэтому давайте разберемся с теми которые не входят в большую шестерку с половиной (Chrome, Firefox, Edge, Opera, Safari и IE). Проценты глобального использования приведены для последней версии на момент написания статьи
- QQ Browser, 0.2% — https://en.wikipedia.org/wiki/QQ_browser
Китайский браузер от Tencect, раньше был на Trident, теперь на Chromium, но нет инфы по поддержке на compat-table и даже на китайской википедии последняя версия от 2017 года (Chromium 53) - UC Browser, 2.88% — https://en.wikipedia.org/wiki/UC_Browser
Очень популярный и еще один китайский браузер для Android, но данных по поддержке также нет и я даже не знаю на каком движке он работает. Если кто-то в курсе — напишите - Baidu Browser, 0%
И еще один китайский браузер, на этот раз от Baidu (китайский гугл). Судя по всему не получил распространения даже в Китае и данных по поддержке фич естественно нет. - KaiOS Browser, 0.2% — https://www.kaiostech.com/
KaiOS это очень интересный проект — операционная система для очень дешевых смартфонов, призванная сделать технологии доступнее. К сожалению данных по поддержке нет, но я полагаю что можно наладить контакты с разработчиками и узнать. Вообще, rule of a thumb что если данных нет — можно считать что браузер поддерживает ES5 - Samsung Internet, 2.73% — https://en.wikipedia.org/wiki/Samsung_Internet
Довольно популярный браузер под Android от Sansung, основан на Chromium (11.1 соответсвует Chromium 75) - Opera Mini all, 1.17%
Очень популярный в Африке браузер (там сильный перевес дешевых мобильных устройств и довольно дорогой трафик). Не поддерживает ES6 в режиме Extreme saving поэтому можно считать что поддерживает только ES5
Что делать с ними ^ решать конечно вам, но я крайне советую стараться поддерживать как можно больше браузеров, это помогает сохранять их разнообразия и не загоняет нас в ловушку IE6 (кхе-кхе Chrome)
Это уже очень много информации, но мы все еще не коснулись таких вещей как
- autoprefixer и CSS
- Свои собственные данные по usage
- Несколько конфигураций и бандлов
- Продвинутые опции
useBuiltIns
Я надеюсь мы когда-нибудь покроем это в второй части, а пока, всем peace, и пусть ваша избушка не развалится ни в одном браузере! А если вы заинтересовались курсом, то можете узнать о нем подробнее по ссылке.