CSS-in-JS, будучи не совсем новой технологией и реализованной во множестве библиотек, до сих пор вызывает сомнения и споры в целесообразности ее использования. Свои точки над «i» в спорах о CSS-in-JS в общем, и о styled-components в частности, расставил еще год назад, ( 27 Apr 2017 ), и Gajus Kuizinas, автор react-css-modules и babel-plugin-react-css-modules, во все еще, на мой взгляд, актуальной публикации «Прекратите использовать CSS-in-JavaScript в веб-разработках».
Ниже предлагается слегка сокращенный ее перевод с некоторыми добавлениями и выводами:
CSS никуда не уходит. Во множестве проектов выбор в пользу стилизации в JavaScript делается ошибочно. Мы приведем перечень распространенных заблуждений(мифов) и соответствующих им решений посредством CSS.
История Css и JavaScript
CSS был создан для описания представления документа, написанного на языке разметки. JavaScript был создан, как «язык-клей» для сборки компонентов, таких, как изображения и плагины. Спустя годы, JS рос и изменялся, приспосабливаясь ко все новым сферам применения. И, как результат, мы сегодня живем в эру одностраничных приложений (SPA), приложений собранных из компонентов.
А что же с CSS?
Приведем цитату из документации styled-components:
Проблема обычного CSS состоит в том, что он был создан в эру, когда веб состояла из документов. Web была создана для обмена по преимуществу научными документами в 1993 году и CSS был введен, как решение для стилизации этих документов. Однако, сегодня мы разрабатываем развитые, интерактивные, ориентированные на пользователя приложения, а CSS просто не предназначен для этого.
Это не так.
CSS уже учитывает все требования современных пользовательских интерфейсов. Количество новых функций, реализованных в последнее десятилетие таково, что их все невозможно здесь даже перечислить (pseudo-classes, pseudo-elements, CSS variables, media queries, keyframes, combinators, columns, flex, grid, computed values, …).
С точки зрения пользовательского интерфейса, «component» — это изолированный фрагмент документа (<button />- это «component»). CSS создан для стилизации документов, а документ охватывает все компоненты. В чем же проблема?
Как говорится: «Используйте правильный инструмент для работы».
Styled-components
styled-components дает возможность написания CSS в JavaScript используя т.н. тегированные шаблонные строки. Библиотека удаляет сопоставление между компонентами и стилями — компонент превращается в конструкцию с низкоуровневой стилизацией, например:
// Create a <Title> react component that renders an <h1> which is
// centered, palevioletred and sized at 1.5em
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
styled-components сегодня пользуется популярностью и известна, как новый способ стилизации компонентов в React, хотя иногда ее представляют даже, как «вершину развития CSS»:
CSS Evolution: From CSS, SASS, BEM, CSS Modules to Styled Components.
Но давайте уясним следующее: styled-components — это всего лишь надстройка над CSS. Эта библиотека разбирает стили CSS, определенные вами в JavaScript и создает соответствующие JSX элементы.
Популярность styled-components сопряжена со множеством заблуждений. Обзор ответов программистов (найденных на IRC, Reddit и Discord) на вопрос о причинах, побудивших их применить эту библиотеку, позволил составить список наиболее упоминаемых. Назовем их мифами.
Миф #1: styled-components решает проблемы глобального пространства имен и конфликтов стилей
Это миф, потому что звучит так, якобы упомянутая проблема еще никак не решалась. Однако CSS Modules, Shadow DOM и бесчисленные соглашения по именованиям ( такие как BEM) предложили решения проблемы давным давно.
styled-components (точно так же, как и CSS modules) снимает с человека проблему ответственности за наименования. Человеку свойственно ошибаться, компьютеры ошибаются не столь часто.
Сам по себе, это еще недостаточно веский довод для использования styled-components.
Миф 2: Использование styled-components дает возможность получать более компактный код
В подтверждение часто приводят пример:
<TicketName></TicketName>
<div className={styles.ticketName}></div>
Прежде всего — это не имеет значения. Разница незначительна.
Во-вторых, это неправда. Общее количество символов зависит от имени стиля.
<TinyBitLongerStyleName></TinyBitLongerStyleName>
<div className={styles.longerStyleName}></div>
То же самое применимо и к созданию стилей, что мы увидим позже в этой статье
(Миф5: styled-components облегчает условную стилизацию компонентов). styled-components выигрывает в краткости только в случаях самых базовых компонентов.
Миф 3. Использование styled components вынуждает больше думать о семантике
Сам первоначальный посыл ошибочен. Стилизация и семантика представляют собой разные проблемы и требуют различных решений. Прочтите, например, это Adam Morse (mrmrs).
Тем не менее, рассмотрим:
<PersonList>
<PersonListItem>
<PersonFirstName>Foo</PersonFirstName>
<PersonLastName>Bar</PersonLastName>
</PersonListItem>
</PersonList>
Семантика занимается применением правильных тегов для разметки. А вы знаете, какие HTML теги будут использованы для рендеринга такого компонента? Нет, не знаете.
Cравните:
<ol>
<li>
<span className={styles.firstName}>Foo</span>
<span className={styles.lastName}>Bar</span>
</li>
</ol>
Миф 4: Проще расширять стили
Сравните:
const Button = styled.button`
padding: 10px;
`;
const TomatoButton = Button.extend`
color: #f00;
`;
Замечательно! Но то же самое вы можете сделать и в CSS (или использовать CSS module composition, а также SASS inheritance mixin @extend).
button {
padding: 10px;
}
button.tomato-button {
color: #f00;
}
И чем проще первый вариант?
Миф 5: Облегчается условная стилизация компонентов
Идея состоит в том, что вы можете стилизовать элементы используя props, например:
<Button primary />
<Button secondary />
<Button primary active={true} />
Это имеет смысл в React. В конце концов, поведение компонента контролируется через props. Есть ли смысл в прямой привязке prop значений к стилям? Может быть. Но давайте рассмотрим имплементацию такого компонента:
styled.Button`
background: ${props => props.primary ? '#f00' : props.secondary ? '#0f0' : '#00f'};
color: ${props => props.primary ? '#fff' : props.secondary ? '#fff' : '#000'};
opacity: ${props => props.active ? 1 : 0};
`;
Создание условных стилей при помощи JavaScript дает массу возможностей. Однако, это также означает, что стили будет намного сложнее интерпретировать. Сравните с CSS:
button {
background: #00f;
opacity: 0;
color: #000;
}
button.primary,
button.seconary {
color: #fff;
}
button.primary {
background: #f00;
}
button.secondary {
background: #0f0;
}
button.active {
opacity: 1;
}
В этом случае, CSS короче (229 символов против 222) и проще для понимания (субъективно). Более того, в CSS вы могли бы использовать препроцессор, чтобы получить еще более короткий и сгрупированный результат:
button {
background: #00f;
opacity: 0;
color: #000;
&.primary,
&.seconary {
color: #fff;
}
&.primary {
background: #f00;
}
&.secondary {
background: #0f0;
}
&.active {
opacity: 1;
}
}
Миф 6: Улучшается организация кода
Некоторые говорят о том, что им нравится styled-components, потому что появляется возможность размещать стили и скрипты в одном файле. Можно понять, что наличие множества файлов для одного компонента может показаться утомительным, но впихивание стилей и разметки в единый файл — это ужасно. Это не только системе контроля версий усложнит отслеживание изменений, но и приведет к бесконечному «скроллингу» на любом элементе посложнее простой кнопки.
Если же вам непременно нужно разместить CSS и JavaScript в одном и том же файле, попробуйте css-literal-loader. Последнее позволит вам собирать стили во время «build» посредством extract-text-webpack-plugin и использовать стандартную конфигурацию лоадера для обработки CSS.
Миф 7: «Developer Experience (DX) » становится прекрасным. Инструмент потрясающий!
Очевидно, что вы не использовали styled-components.
- Если что-то пошло не так со стилями, все приложение «падает» с длинной стековой трассировкой ошибки. Противоположность этому — CSS, где ошибка в стилях приведет просто к неправильному отображению элемента.
- У элементов отсутствуют различаемые className, поэтому при отладке придется бесконечно переключаться между React element tree и DevTools DOM tree.
- И хотя плагины для линтинга, подсветки кода, автозавершения кода, и другие подобные «прелести» уже существуют и для styled-components, они все еще могут быть недоступны для вашего IDE. Если вы работаете с финансами в правительственном агенстве, есть шансы, что Atom IDE будет недоступна.
Миф 8: Все становится «шустрее», все становится «легче»
- Как выяснилось, styled-components стили не могут быть экстрагированы в статический
CSS файл (например используя extract-text-webpack-plugin). Это значит, что браузер не сможет начать интерпретацию стилей до тех пор, пока styled-components не «распарсит» их и не добавит в DOM. - Совмещение стилей и скриптов в одном файле означает, что раздельное их кеширование невозможно.
- Природа styled-components такова, что все они обернуты в дополнительный HOС. А это ненужное понижение производительности. Такой же недостаток привел к прекращению поддержки react-css-modules и появлению babel-plugin-react-css-modules.
- Из-за того же HOC, серверный рендеринг приводит к значительно большему размеру разметки документа.
- Даже и не пробуйте анимировать компоненты используя динамические стили, а не keyframes.
Миф 9: Появляется возможность разработки «responsive» компонентов
Речь идет о способности стилизовать компонент на основе окружающей среды, например, размеров родительского контейнера, количества наследников и т. д.
Прежде всего, styled-components не имеют ничего общего с этим.
Это выходит за рамки проекта. В данном случае лучше прямо установить значения стиля компонента, чтобы избежать дополнительных нагрузок на процессор.
Минуточку, однако!
Большинство, если не все из этих проблем, могут быть решены в перспективе то ли сообществом, изменениями в React или в самой styled-components. Только зачем? CSS уже широко поддерживается, имеется солидное сообщество и… просто работает.
Но не нужно всегда избегать применения CSS-in-JavaScript или styled-components.
Есть кое-что, что делает styled-components хорошей библиотекой — это лучшая кросс-платформенная поддержка.
Просто не используйте styled-components исходя из ошибочных представлений.
Пара мнений от заинтересованных лиц
Talia Marcassa: Что мы нашли разочаровывающим в styled-components?
Применяя styled-components в нашем проекте мы обнаружили, что имеются некоторые стилевые правила, которые трудно внедрять в styled-components; например, правила, которые управляют размещением элементов на странице ( margins или display свойство). Это трудно поддавалось стандартизации, так что мы по-прежнему были вынуждены в значительной степени опереться на простой CSS для размещения компонентов в потоке элементов.
… хотя и были сложности в применении styled-components, библиотека помогла нам уменьшить кодовую базу.
Prometheus: В итоге что?
(комментарий к «Эволюция CSS: от CSS, SASS, BEM и CSS–модулей до styled-components»)
… Никакая эволюция CSS тут не значится, эволюционируют инструменты.
На мой взгляд, проблемы никакой нету, когда версткой занимается верстальщик, а программист — программированием.
А вот когда программисты занимаются версткой — вот тогда начинается весь этот зоопарк, который вместо ПРОСТОТЫ с дальнейшей поддержкой, дает лишнюю головную боль.
…
Пишите на чистом CSS, а инструментам отведите роль пространства имен и сборщика — тогда будет всем счастье…
И в заключение, слово автору:
Gajus Kuizinas: — Так что, все-таки, использовать?
- Еще слишком рано говорить о Shadow DOM, ввиду недостаточно глобальной поддержки.
- Используйте CSS с каким-либо соглашением об именовании (например BEM).
- Если вас волнует проблема коллизий имен классов или вы слишком ленивы, чтобы использовать BEM, используйте CSS Modules.
- Если вы работаете с React — рассмотрите возможность использования babel-plugin-react-css-modules.
- Если же вы работаете с React native, styled-components — это то, что надо.