О преимуществах встраивания CSS в JS
Этот пост является развернутым ответом на вопросы из этого разговора в Твиттере. Автор оригинала, Сунил Пай, является автором относительно популярной библиотеки glamor и работает разработчиком в Facebook.
Каким образом Javascript оказывается более удобным чем CSS? Как написание CSS внутри JS делает его более поддерживаемым?
Буду счастлив пообщаться на эту тему. Сразу скажу, что у CSS-in-JS решений есть накладные расходы, но обычно эта цена оправдана теми преимуществами, которые они приносят. Иногда они могут быть полезными, но также это не значит, что CSS-in-JS должен использоваться всегда и везде.
Итак, самое главное в CSS-in-JS (cij) это CSS-селекторы. Наибольшее преимущество cij состоит в том, что компьютеры могут генерировать эти селекторы автоматически. Конвенции типа OOCSS и т.п. в принципе хороши, но они основываются на вручную написанных селекторах, которым сложно обеспечить уникальность в общем пространстве имен, потому что там всегда есть шанс на пересечение. Если не случится сейчас, то может произойти спустя года три, когда ваша команда разрастется, а обстоятельства поменяются. Эта ситуация усугубляется с использованием сторонних библиотек. Ограничение области действия CSS-селекторов также может достигаться использованием CSS-модулей, которые пользуются популярностью именно за эту возможность.
Тем не менее при всех этих подходах селекторы очень сложно статически анализировать и находить, какие из них сломаны (или содержат опечатки), а это означает что их придется проверять вручную в процессе code-review, что снизит эффективность команды. Мы должны пользоваться помощью компьютеров и не рассчитывать на безупречность и безошибочность людей (потому что это не так). CSS-in-JS дает возможность автоматизировать генерацию уникальных селекторов и идентификаторов.
Кроме того, усиленный контроль за CSS-селекторами открывает новые возможности, которые не были легко доступны раньше. Например, мы можем элементарно реализовать извлечение критического CSS, сопоставляя блоки HTML с нужными им стилями, так что мы сможем загрузить на страницу всего 1–2 Кб CSS, который нужен для изначального отображения страницы. Безо всякого рантайма! Фреймворки типа Gatsby и Next активно этим пользуются, чтобы улучшить показатели производительности в проектах на их основе. Они встраивают критический CSS прямо в загружаемый HTML просто потому что он весит настолько мало, что это будет лучше лишнего запроса, блокирующего загрузку страницы. Это значительно сокращает время первоначальной отрисовки страницы. Более того, это также способствует решению проблем размера загружаемого Javascript! Выгода приходит от применения критического CSS, а также использования динамического import()
, разделения кода и удалению неиспользуемого кода. (В противовес постоянно растущим легаси файлам стилей, в которые разработчики только добавляют новый код, боясь трогать существующий. Здесь есть немного аналитики на эту тему.)
Аналогичная ситуация с реализацией тем оформления. Знаете ли вы, что CSS-переменные, даже будучи очень интересной штукой, так и не взлетели до тех сценариев использования, для которых они изначально задумывались, а все потому что способы фоллбека для старых браузеров оказались либо очень сложными, либо неполноценными? Это означает, что они обычно используются как глобальные константы, но очень редко как переменные, значение которых определяется в браузере динамически. Наличие рантайма для работы с CSS означает, что вы сможете передавать значения из JS в стили, делая это возможным.
В самом деле, вам это понравится: вы сможете, наконец, по-честному использовать CSS-переменные во всех браузерах, то есть нативные возможности без рантайма в браузерах, которые их поддерживают, и специальный рантайм для старых браузеров. (Я написал здесь об этом подробнее, и — если я правильно понимаю — это единственная библиотека, которая добилась этого).
В ситуации сложных SPA с кучей компонентов и асинхронной загрузкой ресурсов, таких как скрипты и стили, вы не можете гарантировать точный порядок загрузки стилей, так что либо вам придется создавать какие-то рантайм решения для гарантирования порядка стилей, или просто использовать !important
, даже если вы придерживаетесь какой-то CSS-методологии. Например, у вас есть элемент с class="a b”
, но классы a
и b
определены в разных файлах стилей, то вы не сможете быть уверены в финальном виде блока, если у вас не задан четко порядок следования файлов стилей. Кодовая база Facebook содержит тысячи использований !important
, даже несмотря на то, что код писался квалифицированными программистами с использованием принципов SOLID и хорошим взаимодействием с командой дизайнеров.
Здесь часто говорится об обращение с CSS как будто это Javascript — учитывая, что 10 лет назад мы уже решали аналогичные проблемы для JS — библиотеки и модули регистрировали себя в глобальное пространство имен ($
и тому подобные) и нам приходилось быть довольно внимательными с порядком подключение скриптов в HTML. Но мы не застряли там навсегда — сейчас мы используем модули, и системы сборки сшивают их вместе в гарантированно правильном порядке. Это происходит просто и прозрачно для нас.
Наблюдая за реальными проектами, я также заметил, что вы все еще можете использовать традиционные архитектурные подходы (типа OOCSS или SMACSS и т.п.) в мире CSS-in-JS — элементы тех архитектур представляются здесь JS-объектами, вместо блоков селектор+стили. Я придерживаюсь такого подхода и он работает для меня хорошо. Здесь об этом можно почитать больше.
Также я хорошо осведомлен о недостатках CSS-in-JS. В самом деле, именно поэтому здесь нет какой то одной «каноничной» библиотеки, которая представляет CSS-in-JS — это спектр разных решений, от ванильного статического CSS с одной стороны до полностью динамических библиотек, типа styled-components с другой. Препроцессоры типа SASS или LESS могут показаться перпендикулярными этому спектру, потому что они теоретически могут использоваться любой из этих библиотек на усмотрение их авторов. Каждая из этих библиотек имеет свои недостатки — некоторые занимаются извлечением стилей на этапе сборки, чтобы не было расходов в рантайме, некоторые сфокусированы на правильности или удобстве для разработчиков, другие заточены на эффективную реализацию сложных анимаций, и так далее и тому подобное. Это многообразие — естественная реакция на необходимость разных решений для разных рабочих задач, в чересчур быстро развивающийся индустрии. И это происходит не только с библиотеками — разработчики веб-стандартов (ShadowDOM и других) доблестно стараются решить эти проблемы тоже, но их решение также имеет недостатки, наименьший из которых, это то что они пока не везде доступны, что делает это неприемлемым для использования во множестве команд.
Раньше у меня были сильные чувства по этому поводу, но я умерил свои взгляды на протяжении последних нескольких месяцев. Оказывается, что истина на самом деле лежит где-то посередине — это зависит от команд, требований, времени, документации и многих других факторов. Более того, я не думаю, что этот текст представляет «финальную форму» этой идеи. Мы должны поощрять эксперименты в этой области, лишь бы мы смогли узнать, что еще мы можем сделать лучше, и даже встроить некоторые из этих вещей в браузеры.
P.S. Слишком поздно осознал, что забыл отметить галочку «Перевод», поэтому опубликовалось вот так. Ссылка на оригинал.
Хабр, почини интерфейс, почему нельзя добавить флажок перевода уже после публикации?