Урок 26: Популярные подходы к стилизации в React

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

В этом уроке мы разберем три основных подхода к стилизации React-приложений, классические CSS-модули, современные CSS-in-JS библиотеки на примере Styled Components, а также бегло взглянем на готовые библиотеки компонентов. Я подробно расскажу о плюсах и минусах каждого метода, покажу много практических примеров и, самое главное, помогу вам понять, какой подход выбрать для вашего следующего проекта.

CSS-модули

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

Идея проста. Каждый файл стилей (например, Button.module.css) считается независимым модулем. Имена классов, которые вы в нем объявляете, при сборке проекта автоматически преобразуются в уникальные. Это означает, что класс .btn в вашем компоненте Button и класс .btn в компоненте Header будут скомпилированы в разные, уникальные имена, например, Button_btn__a1b2c и Header_btn__x3y4z. Таким образом, стили изолированы в рамках своего компонента и вы можете не бояться конфликтов.

Давайте посмотрим, как это работает на практике. Представьте, что у нас есть простой компонент кнопки.

Пример кода: Обычный CSS (проблема)

Button.css:

css
.btn {
  padding: 10px 20px;
  background-color: blue;
  color: white;
  border: none;
  border-radius: 4px;
}

Button.js:

jsx
import './Button.css';

function Button() {
  return <button className="btn">Нажми меня</button>;
}

Пока все хорошо. Но если в другом файле, например, Header.css, мы тоже объявим класс .btn с другими стилями, они начнут конфликтовать. С CSS-модулями этой проблемы нет.

Пример кода: Использование CSS-модулей

Переименуем наш файл стилей, добавив суффикс .module.cssButton.module.css. Содержимое может остаться тем же.

Button.module.css:

css
.btn {
  padding: 10px 20px;
  background-color: blue;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.btn:hover {
  background-color: darkblue;
}

Теперь импортируем стили в наш компонент иначе:

Button.js:

jsx
import styles from './Button.module.css';

function Button() {
  return (
    <button className={styles.btn}>
      Нажми меня
    </button>
  );
}

styles это обычный JavaScript-объект, который импортируется из CSS-файла. Ключи в этом объекте, это имена классов, которые вы написали в CSS-файле (btn), а значения это те самые сгенерированные уникальные имена. В этом и заключается вся суть. Вы продолжаете писать привычный CSS, но получаете гарантированную изоляцию стилей.

Плюсы CSS-модулей:

  • Изоляция стилей. Полное отсутствие конфликтов имен классов.

  • Простота освоения. Если вы знаете CSS, вам не нужно учить новый синтаксис.

  • Стабильность и производительность. Это «скомпилированный» подход. На клиент не приходят лишние библиотеки, все стили это статические CSS-файлы.

  • Совместимость. Отлично работает с SASS/SCSS (просто используйте [name].module.scss).

Минусы CSS-модулей:

  • Динамические стили. Работа с пропсами для изменения стилей становится менее интуитивной и требует передачи нескольких классов или inline-стилей.

  • Глобальные стилы. Для сброса стилей или шрифтов все равно нужен отдельный глобальный CSS-файл.

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

Styled Components: CSS-in-JS и динамические стили

Это подход, при котором мы пишем стили не в отдельных .css файлах, а прямо внутри JavaScript-кода, используя специальные библиотеки. Самая популярная из них, это Styled Components. Её суть заключается в том, что компонент должен включать в себя всё: и логику и разметку и стили.

Styled Components позволяет создавать React-компоненты, которые имеют свои стили, «вшитые» прямо в них. Вы описываете стили, используя шаблонные литералы (template literals), те самые строки в обратных кавычках. Внутри этих строк вы пишете обычный CSS. В результате вы получаете готовый React-компонент, который можно использовать как любой другой.

Что делает Styled Components по-настоящему мощными? Они идеально подходят для создания динамических стилей, которые зависят от пропсов. Давайте сразу создадим кнопку, которая может менять свой цвет в зависимости от переданного пропса variant.

Сначала установим библиотеку npm install styled-components

Пример кода: Базовое использование Styled Components

Button.js:

jsx
import styled from 'styled-components';

// Создаем компонент StyledButton, который будет рендерить тег <button> с нашими стилями.
const StyledButton = styled.button`
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;

  // Динамические стили на основе пропса `variant`
  background-color: ${props => {
    if (props.variant === 'primary') return 'blue';
    if (props.variant === 'danger') return 'red';
    return 'gray';
  }};

  color: white;

  // Псевдоклассы тоже работают!
  &:hover {
    opacity: 0.8;
  }
`;

// Используем наш стилизованный компонент как обычный
function Button({ variant, children }) {
  return (
    <StyledButton variant={variant}>
      {children}
    </StyledButton>
  );
}

export default Button;

Теперь мы можем использовать нашу кнопку в другом компоненте:

App.js:

jsx
import Button from './Button';

function App() {
  return (
    <div>
      <Button variant="primary">Основная кнопка</Button>
      <Button variant="danger">Опасная кнопка</Button>
      <Button>Кнопка по умолчанию</Button>
    </div>
  );
}

Как видите, стили напрямую зависят от пропсов, которые мы передаем. Это очень читаемо и удобно. Вся мощь JavaScript теперь доступна вам прямо внутри CSS!

Плюсы Styled Components:

  • Полная инкапсуляция. Стили не просто изолированы, они являются частью компонента.

  • Динамические стили. Легко менять стили в зависимости от пропсов и состояния.

  • Удаление неиспользуемого кода. Если компонент не используется, его стили тоже не попадут в финальную сборку.

  • Отличная темадизация. Библиотека предоставляет удобный API (ThemeProvider) для создания тем оформления.

Минусы Styled Components:

  • Runtime накладные расходы. Стили вычисляются на клиенте, что может незначительно влиять на производительность очень больших приложений.

  • Усложнение DevTools. В инструментах разработчика браузера вы увидите много сгенерированных классов, что может быть менее наглядно, чем семантичные имена классов из CSS-модулей.

  • Зависимость от библиотеки. Вы добавляете еще одну зависимость в проект.

Styled Components это фантастический выбор для проектов, где важна динамическая тема, где компоненты должны гибко адаптироваться и где разработчикам нравится философия «всё в одном месте».

Библиотеки компонентов

Иногда вам не нужно писать свои собственные стилизованные кнопки, карточки и модальные окна. Иногда скорость разработки и единообразие дизайна гораздо важнее. Именно для этого и существуют библиотеки компонентов, такие как Material-UI (MUI), Ant Design, Chakra UI и многие другие.

Эти библиотеки предоставляют вам готовые, часто очень красивые и функциональные компоненты, которые вы можете просто импортировать и использовать. Они уже протестированы, доступны и следуют определенным гайдлайнам дизайна (например, Material Design от Google в случае с MUI).

Давайте рассмотрим, как бы выглядела наша кнопка с использованием библиотеки Material-UI.

Сначала устанавливаем: npm install @mui/material @emotion/react @emotion/styled

Пример кода: Использование Material-UI

Button.js:

jsx
import Button from '@mui/material/Button';

function MyAppButton({ variant, children }) {
  return (
    <Button variant={variant} color="primary">
      {children}
    </Button>
  );
}

export default MyAppButton;

App.js:

jsx
import MyAppButton from './Button';
import Stack from '@mui/material/Stack'; // Компонент для компоновки

function App() {
  return (
    <Stack direction="row" spacing={2}>
      <MyAppButton variant="contained">Основная кнопка</MyAppButton>
      <MyAppButton variant="outlined">Контурная кнопка</MyAppButton>
      <MyAppButton variant="text">Текстовая кнопка</MyAppButton>
    </Stack>
  );
}

И всё! Вы получили три красиво стилизованные кнопки с интерактивными состояниями (hover, focus, etc.), не написав ни строчки CSS. Библиотеки компонентов экономят колоссальное количество времени.

Плюсы библиотек компонентов:

  • Скорость разработки. Не нужно изобретать велосипед.

  • Единообразие дизайна. Все компоненты следуют одной дизайн-системе.

  • Доступность (a11y). Компоненты часто уже имеют встроенную доступность (правильные ARIA-атрибуты, управление с клавиатуры).

  • Богатая функциональность. Сложные компоненты вроде таблиц, календарей, модальных окон уже реализованы.

Минусы библиотек компонентов:

  • Размер бандла. Вы добавляете в проект очень много чужого кода, что увеличивает итоговый размер приложения.

  • Ограниченность кастомизации. Выйти за рамки предусмотренного дизайна может быть сложно. Иногда приходится бороться с библиотекой, чтобы переопределить её стили.

  • Зависимость от внешней зависимости. Обновления библиотеки могут ломать ваш код.

Библиотеки компонентов это идеальный выбор для внутренних инструментов (admin panels), прототипирования и проектов, где вы готовы пожертвовать уникальностью дизайна ради скорости.

Как выбрать подход для своего проекта?

Итак, у нас есть три мощных инструмента. Какой же выбрать? Давайте систематизируем знания и я дам вам несколько рекомендаций.

Я всегда задаю себе и своей команде несколько ключевых вопросов перед началом проекта:

  • Насколько уникальным должен быть дизайн?

    • Очень уникальный (бренд-центричный.: CSS-модули или Styled Components. Они дают полную свободу.

    • Можно использовать готовую дизайн-систем.: Библиотека компонентов (Material-UI, Ant Design).

  • Какой размер команды и её опыт?

    • Небольшая команда, сильный CSS-эксперт: CSS-модули.

    • Команда, которая любит JavaScript и хочет инкапсуляции: Styled Components.

    • Команда, где нет сильного фронтенд-дизайнера, но нужно быстро сделать качественный интерфейс: Библиотека компонентов.

  • Каковы требования к производительности и размеру бандла?

    • Критически важны: CSS-модули (минимальные накладные расходы).

    • Приемлем небольшой runtime: Styled Components.

    • Размер бандла не является главным приоритетом: Библиотека компонентов.

  • Нужна ли темизация (светлая/тёмная тема)?

    • Да и гибкая: Styled Components с ThemeProvider, то это лучший выбор.

    • Да, но в рамках библиотеки: MUI и другие имеют встроенную поддержку тем.

Мой личный сводный совет:

  1. Для большинства средних и крупных проектов с уникальным дизайном я предпочитаю CSS-модули (с SCSS). Это надежно, производительно и не мешает масштабированию.

  2. Для проектов, где динамика стилей (сложные анимации, темы, стили, сильно зависящие от пропсов) выходит на первый план, я выбираю Styled Components.

  3. Когда нужно «сделать вчера» или разрабатывается internal tool, я без раздумий беру библиотеку компонентов (чаще всего Chakra UI или MUI).

Например, можно использовать библиотеку компонентов для основной части приложения, а для уникальных, промо-блоков написать свои компоненты с помощью Styled Components.

Практические задачи для закрепления

Чтобы материал точно усвоился, я подготовил для вас несколько практических задач. Постарайтесь выполнить их самостоятельно.

Задача 1: Создайте компонент карточки с использованием CSS-модулей

Создайте компонент Card, который отображает изображение, заголовок и текст. Используйте CSS-модули для стилизации. Убедитесь, что стили карточки не влияют на другие элементы страницы.

Решение (Card.module.css):

css
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  max-width: 300px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  font-family: sans-serif;
}

.cardImage {
  width: 100%;
  height: 160px;
  object-fit: cover;
}

.cardContent {
  padding: 16px;
}

.cardTitle {
  margin-top: 0;
  margin-bottom: 8px;
  font-size: 1.2rem;
}

.cardText {
  color: #666;
  line-height: 1.5;
}

Решение (Card.js):

jsx
import styles from './Card.module.css';

function Card({ imageUrl, title, text }) {
  return (
    <div className={styles.card}>
      <img src={imageUrl} alt={title} className={styles.cardImage} />
      <div className={styles.cardContent}>
        <h3 className={styles.cardTitle}>{title}</h3>
        <p className={styles.cardText}>{text}</p>
      </div>
    </div>
  );
}

export default Card;

Задача 2: Создайте «адаптивную» кнопку с использованием Styled Components

Создайте компонент кнопки AdaptiveButton, который меняет свой размер в зависимости от пропса size (smallmediumlarge). Также добавьте пропс disabled, который делает кнопку полупрозрачной и отключает курсор.

Решение (AdaptiveButton.js):

jsx
import styled from 'styled-components';

const getPadding = (size) => {
  switch (size) {
    case 'small': return '8px 12px';
    case 'large': return '16px 24px';
    default: return '12px 18px'; // medium
  }
};

const StyledButton = styled.button`
  padding: ${props => getPadding(props.size)};
  background-color: blue;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: ${props => props.size === 'small' ? '0.8rem' : '1rem'};

  /* Динамические стили для disabled */
  opacity: ${props => props.disabled ? 0.5 : 1};
  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};

  &:hover {
    background-color: ${props => props.disabled ? 'blue' : 'darkblue'};
  }
`;

function AdaptiveButton({ size = 'medium', disabled = false, children }) {
  return (
    <StyledButton size={size} disabled={disabled}>
      {children}
    </StyledButton>
  );
}

export default AdaptiveButton;

Задача 3: Создайте простую панель навигации с помощью библиотеки компонентов

Выберите любую понравившуюся библиотеку (например, Material-UI) и создайте компонент Navbar. Он должен содержать логотип (просто текст) и несколько ссылок (например, «Главная», «О нас», «Контакты»).

Решение (с использованием MUI):

jsx
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';

function Navbar() {
  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static">
        <Toolbar>
          <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
            Мой Сайт
          </Typography>
          <Button color="inherit">Главная</Button>
          <Button color="inherit">О нас</Button>
          <Button color="inherit">Контакты</Button>
        </Toolbar>
      </AppBar>
    </Box>
  );
}

export default Navbar;

На сегодня всё! Мы разобрали три столпа стилизации в React-приложениях. Вы теперь знаете сильные и слабые стороны каждого подхода и сможете сделать осознанный выбор для своего проекта.

Не забывайте, что все уроки моего курса структурированы и идут от простого к сложному, чтобы вы могли плавно и уверенно стать востребованным React-разработчиком.

Вернуться к полному курсу «React для начинающих»

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий