Styled-Components: библиотека CSS-in-JS для современного Интернета

Опубликовано: 2022-03-11

CSS был разработан для документов, которые должны были содержать «старую сеть». Появление препроцессоров, таких как Sass или Less, показывает, что сообществу нужно больше, чем предлагает CSS. Поскольку веб-приложения со временем становятся все более и более сложными, ограничения CSS становятся все более заметными, и их становится все труднее смягчить.

Styled-components использует возможности полного языка программирования — JavaScript — и его возможности определения области, чтобы помочь структурировать код в компоненты. Это помогает избежать распространенных ошибок при написании и поддержке CSS для больших проектов. Разработчик может описать стиль компонента без риска возникновения побочных эффектов.

В чем проблема?

Одним из преимуществ использования CSS является то, что стиль полностью отделен от кода. Это означает, что разработчики и дизайнеры могут работать параллельно, не мешая друг другу.

С другой стороны, styled-components облегчает попадание в ловушку тесной связи стиля и логики. Макс Штойбер объясняет, как этого избежать. Хотя идея разделения логики и представления определенно не нова, при разработке компонентов React может возникнуть соблазн использовать ярлыки. Например, легко создать компонент для кнопки проверки, который обрабатывает действие щелчка, а также стиль кнопки. Требуется немного больше усилий, чтобы разделить его на два компонента.

Контейнерная/презентационная архитектура

Это довольно простой принцип. Компоненты либо определяют внешний вид вещей, либо управляют данными и логикой. Очень важным аспектом компонентов презентации является то, что они никогда не должны иметь никаких зависимостей. Они получают свойства и отображают DOM (или дочерние элементы) соответственно. Контейнеры, с другой стороны, знают об архитектуре данных (состояние, избыточность, поток и т. д.), но никогда не должны нести ответственность за отображение. Статья Дэна Абрамова — очень хорошее и подробное объяснение этой архитектуры.

Вспоминая SMACSS

Хотя «Масштабируемая и модульная архитектура для CSS» представляет собой руководство по стилю для организации CSS, основной концепцией является та, которой по большей части автоматически следуют стилизованные компоненты. Идея состоит в том, чтобы разделить CSS на пять категорий:

  • База содержит все общие правила.
  • Цель макета — определить структурные свойства, а также различные разделы контента (например, заголовок, нижний колонтитул, боковая панель, контент).
  • Модуль содержит подкатегории для различных логических блоков пользовательского интерфейса.
  • Состояние определяет классы модификаторов для указания состояний элементов, например, поле с ошибкой, отключенная кнопка.
  • Тема содержит цвет, шрифт и другие косметические аспекты, которые могут быть изменены или зависеть от предпочтений пользователя.

Сохранять это разделение при использовании стилизованных компонентов несложно. Проекты обычно включают некоторую нормализацию или сброс CSS. Обычно это относится к базовой категории. Вы также можете определить общий размер шрифта, размер строки и т. д. Это можно сделать с помощью обычного CSS (или Sass/Less) или с помощью функции injectGlobal , предоставляемой styled-components .

Для правил макета , если вы используете инфраструктуру пользовательского интерфейса, она, вероятно, будет определять классы контейнеров или систему сетки. Вы можете легко использовать эти классы в сочетании со своими собственными правилами в компонентах макета, которые вы пишете.

За модулем автоматически следует архитектура styled-components , так как стили прикрепляются к компонентам напрямую, а не описываются во внешних файлах. По сути, каждый стилизованный компонент, который вы пишете, будет отдельным модулем. Вы можете написать свой код стиля, не беспокоясь о побочных эффектах.

Состоянием будут правила, которые вы определяете в своих компонентах как переменные правила. Вы просто определяете функцию для интерполяции значений ваших атрибутов CSS. Если вы используете UI-фреймворк, у вас также могут быть полезные классы для добавления к вашим компонентам. Вероятно, у вас также будут правила псевдоселектора CSS (наведение курсора, фокус и т. д.).

Тема может быть просто интерполирована в ваши компоненты. Хорошей идеей будет определить тему как набор переменных, которые будут использоваться во всем приложении. Вы даже можете получить цвета программно (используя библиотеку или вручную), например, для обработки контрастов и бликов. Помните, что в вашем распоряжении вся мощь языка программирования!

Соберите их вместе для решения

Важно держать их вместе, чтобы облегчить навигацию; Мы не хотим упорядочивать их по типу (представление или логика), а скорее по функциональности.

Таким образом, у нас будет папка для всех общих компонентов (кнопки и тому подобное). Остальные должны быть организованы в зависимости от проекта и его функций. Например, если у нас есть функции управления пользователями, мы должны сгруппировать все компоненты, относящиеся к этой функции.

Чтобы применить архитектуру контейнера/представления стилизованных компонентов к подходу SMACSS, нам нужен дополнительный тип компонента: структурный. В итоге мы получаем три вида компонентов; стилизованный, структурный и контейнерный. Так как styled-components украшает тег (или компонент), нам нужен этот третий тип компонента для структурирования DOM. В некоторых случаях можно позволить компоненту-контейнеру обрабатывать структуру подкомпонентов, но когда структура DOM становится сложной и требуется для визуальных целей, лучше их разделить. Хорошим примером является таблица, где DOM обычно становится довольно подробным.

Пример проекта

Давайте создадим небольшое приложение, которое отображает рецепты, чтобы проиллюстрировать эти принципы. Мы можем начать создавать компонент Recipes. Родительский компонент будет контроллером. Он будет обрабатывать состояние — в данном случае список рецептов. Он также вызовет функцию API для получения данных.

 class Recipes extends Component{ constructor (props) { super(props); this.state = { recipes: [] }; } componentDidMount () { this.loadData() } loadData () { getRecipes().then(recipes => { this.setState({recipes}) }) } render() { let {recipes} = this.state return ( <RecipesContainer recipes={recipes} /> ) } }

Он отобразит список рецептов, но ему не нужно (и не нужно) знать, как это сделать. Итак, мы визуализируем еще один компонент, который получает список рецептов и выводит DOM:

 class RecipesContainer extends Component{ render() { let {recipes} = this.props return ( <TilesContainer> {recipes.map(recipe => (<Recipe key={recipe.id} {...recipe}/>))} </TilesContainer> ) } }

Здесь, собственно, мы хотим сделать тайловую сетку. Может быть хорошей идеей сделать фактический макет плитки общим компонентом. Итак, если мы извлечем это, мы получим новый компонент, который выглядит так:

 class TilesContainer extends Component { render () { let {children} = this.props return ( <Tiles> { React.Children.map(children, (child, i) => ( <Tile key={i}> {child} </Tile> )) } </Tiles> ) } }

ПлиткиStyles.js:

 export const Tiles = styled.div` padding: 20px 10px; display: flex; flex-direction: row; flex-wrap: wrap; ` export const Tile = styled.div` flex: 1 1 auto; ... display: flex; & > div { flex: 1 0 auto; } `

Обратите внимание, что этот компонент является чисто презентационным. Он определяет свой стиль и оборачивает все дочерние элементы, которые он получает, внутри другого стилизованного элемента DOM, который определяет, как выглядят плитки. Это хороший пример того, как ваши общие презентационные компоненты будут выглядеть архитектурно.

Затем нам нужно определить, как выглядит рецепт. Нам нужен компонент-контейнер для описания относительно сложной модели DOM, а также для определения стиля, когда это необходимо. Мы заканчиваем это:

 class RecipeContainer extends Component { onChangeServings (e) { let {changeServings} = this.props changeServings(e.target.value) } render () { let {title, ingredients, instructions, time, servings} = this.props return ( <Recipe> <Title>{title}</Title> <div>{time}</div> <div>Serving <input type="number" min="1" max="1000" value={servings} onChange={this.onChangeServings.bind(this)}/> </div> <Ingredients> {ingredients.map((ingredient, i) => ( <Ingredient key={i} servings={servings}> <span className="name">{ingredient.name}</span> <span className="quantity">{ingredient.quantity * servings} {ingredient.unit}</span> </Ingredient> ))} </Ingredients> <div> {instructions.map((instruction, i) => (<p key={i}>{instruction}</p>))} </div> </Recipe> ) } }

Обратите внимание, что контейнер выполняет некоторую генерацию DOM, но это единственная логика, которую он содержит. Помните, что вы можете определять вложенные стили, поэтому вам не нужно создавать стилизованный элемент для каждого тега, требующего стиля. Это то, что мы делаем здесь для имени и количества ингредиента. Конечно, мы могли бы разделить его дальше и создать новый компонент для ингредиента. Это зависит от вас — в зависимости от сложности проекта — определить степень детализации. В данном случае это просто стилизованный компонент, определенный вместе с остальными в файле RecipeStyles:

 export const Recipe = styled.div` background-color: ${theme('colors.background-highlight')}; `; export const Title = styled.div` font-weight: bold; ` export const Ingredients = styled.ul` margin: 5px 0; ` export const Ingredient = styled.li` & .name { ... } & .quantity { ... } `

Для целей этого упражнения я использовал ThemeProvider. Он внедряет тему в реквизиты стилизованных компонентов. Вы можете просто использовать его как color: ${props => props.theme.core_color} , я просто использую небольшую оболочку для защиты от отсутствующих атрибутов в теме:

 const theme = (key) => (prop) => _.get(prop.theme, key) || console.warn('missing key', key)

Вы также можете определить свои собственные константы в модуле и использовать их вместо них. Например: color: ${styleConstants.core_color}

Плюсы

Преимущество использования styled-components в том, что вы можете использовать его так редко, как захотите. Вы можете использовать свой любимый UI-фреймворк и добавлять поверх него стилизованные компоненты . Это также означает, что вы можете легко перенести компонент существующего проекта за компонентом. Вы можете стилизовать большую часть макета с помощью стандартного CSS и использовать styled-components только для повторно используемых компонентов.

Минусы

Дизайнерам/интеграторам стилей потребуется изучить очень простой JavaScript, чтобы обрабатывать переменные и использовать их вместо Sass/Less.

Им также придется научиться ориентироваться в структуре проекта, хотя я бы сказал, что найти стили для компонента в папке этого компонента проще, чем найти правильный файл CSS/Sass/Less, содержащий правило, которое нужно изменить.

Им также нужно будет немного изменить свои инструменты, если они хотят подсветку синтаксиса, линтинг и т. д. Хорошее место для начала — этот плагин Atom и этот плагин babel.