Всё, что вам нужно знать о CSS-in-JS

Перевод статьи All You Need To Know About CSS-in-JS с сайта hackernoon.com для CSS-live.ru, автор — Индрек Ласн

Всё, что вам нужно знать о CSS-in-JS

Прекрасная иллюстрация Анны Дадей

Краткая выжимка статьи — поговорим о компонентном подходе к CSS. Больше нет необходимости в поддержке множества CSS-файлов. CSS-in-JS делает логику CSS абстракцией уровня компонента, а не уровня документа (используя принцип модульности).

Пример со стилизованным React-компонентом

Стилизованный React-компонент


Предположу, что вы слышали термины «CSS-in-JS», «стилизованные компоненты», «Radium», «Aphrodite», но не стали в них вникать, думая: «Ну и зачем это всё нужно? Мне вполне хватает CSS-in-CSS (обычного кода CSS в .css-файлах)

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

CSS-in-JS — непростая тема, полная противоречий, и я призываю к непредвзятой позиции. Посмотрите, может ли этот инструмент улучшить ваш рабочий процесс. Это единственное, что имеет значение.

Мне всегда казалось неудобным иметь дело с огромной папкой, полной файлов стилей. Поэтому я опробовал различные подходы к организации CSS. И я встречал много других людей, ищущих новые идеи. CSS-in-JS — пока лучшее из того, что придумано.

Дадим ей шанс показать себя в деле.

CSS проекта, размер которого уже ближе к среднему

CSS проекта, размер которого уже ближе к среднему


Что такое CSS-in-JS?

JSS — более мощная абстракция над CSS. Он использует Javascript в качестве языка для описания стилей в декларативном и удобном в поддержке виде. Это высокопроизводительный компилятор из JS в CSS, который работает и на стороне сервера, и на стороне клиента в реальном времени. Это низкоуровневая и не привязанная к какому-либо фреймворку библиотека. Она весит около 6KB (миницифированная и сжатая gzip). Ее можно расширить при помощи API для плагинов. — источник

Имейте ввиду, что inline-стили и CSS-in-JS — не одно и тоже! Это две разные вещи. Сейчас продемонстрирую.

Как работают inline-стили

В браузере это добавится к DOM-узлу таким образом:

Как работает CSS-in-JS

В браузере это добавится к DOM-узлу таким образом:

Разница

Видите эту небольшую разницу? CSS-in-JS добавился в виде тега <style> вверху дерева DOM, тогда как inline-стили добавились к DOM-узлу в виде свойств (в атрибут style — прим. переводчика).

Какое это имеет значение?

Не все возможности CSS могут быть реализованы при помощи обработчиков событий JavaScript, невозможно использовать многие из псевдоселекторов (типа :disabled, :before, :nth-child), нельзя назначить стили тегам html и body и так далее.

С CSS-in-JS вся мощь CSS всегда под рукой. Поскольку генерируется настоящий CSS-код (в отличие от inline-стилей, где свойства прописаны в атрибуте style — прим. переводчика), вы можете использовать любые медиавыражения и псевдоселекторы, какие только захотите. Некоторые библиотеки (типа jss, styled-components) даже добавляют поддержку изящных возможностей, не нативных для CSS, типа вложенности.

Блестящая статья, в деталях рассматривающая разницу между этими двумя подходами.

«Да просто пишите CSS-in-CSS и забудьте об этой ерунде.»

Всё верно, мы так и делали с незапамятных времен. Но проблема в том, что современный веб строится не страницами, а компонентами.

CSS никогда и не был предназначен для использования в компонентном подходе. А CSS-in-JS как раз и решает эту проблему. Vue отлично с ней справился, между прочим. Правда, во Vue стили не имеют доступа к состояниям компонентов.

Bob Ross

Для сброса напряжения посмотрите, как Боб Росс рисует камушки :)
(по ссылке откроется анимация, 15 MB — прим. переводчика)

В чем плюсы использования CSS-in-JS?

  • Компонентный подход к CSS. Больше нет необходимости в поддержке множества CSS-файлов. CSS-in-JS делает логику CSS абстракцией уровня компонента, а не уровня документа (используя принцип модульности).
  • CSS-in-JS использует всю мощь JavaScript для расширения возможностей CSS.
  • «Настоящая изоляция CSS-правил». Ограничения видимости селекторов недостаточно. В CSS есть свойства, которые, если не заданы явно, автоматически наследуются от родительских элементов. Но, благодаря плагину jss-isolate, JSS-правила не наследуют свойств.
  • Селекторы с ограниченной областью видимости. В CSS есть только одно глобальное пространство имен. Невозможно избежать коллизий селекторов, если только речь идет не об элементарном проекте. Соглашения о наименованиях, вроде БЭМ, могут помочь в рамках одного проекта, но не тогда, когда мы включаем в него сторонний код. JSS генерирует уникальные имена классов по умолчанию при компиляции JSON-представления в CSS.
  • Браузерные префиксы автоматически проставляются в CSS-правила, так что вам не нужно вообще о них думать.
  • Совместное использование кода. JS и CSS легко используют одни и те же константы и функции.
  • В DOM находятся только те стили, которые на данный момент используются для отображения элементов на экране (react-jss).
  • Удаление мертвого кода.
  • Модульное тестирование CSS.

В чем минусы использования CSS-in-JS?

  • Сначала надо научиться пользоваться.
  • Новые зависимости.
  • Новым участникам команды труднее вникать в то, как организован код. Новичкам фронтенда придется изучать больше всяких инструментов.
  • Отказ от привычного подхода (впрочем, это не обязательно минус).

Плюсы явно перевешивают минусы! Так давайте же дадим CSS-in-JS шанс! Нам нечего терять!

Самые популярные библиотеки CSS-in-JS

Я привожу простой «Hello World!» пример, используя все популярные библиотеки для CSS-in-JS. Выбирайте, какая вам больше нравится, исходя из синтаксиса.

npm trends

npm trends (статистика загрузок, по которой можно отследить популярность раличных инструментов — прим. переводчика)

Styled Components

Styled Components

import React, { Component } from 'react';
import styled from 'styled-components';

const Title = styled.h1`
  color: white;
`;

const Wrapper = styled.div`
    background: black
`

class App extends Component {
  render() {
    return (
        <Wrapper>
            <Title>Hello World!</Title>
        </Wrapper>
    );
  }
}

export default App;

JSS-React

JSS

import React from 'react'
import injectSheet from 'react-jss'

const styles = {
    wrapper: {
        background: 'black'
    },
    title: {
        color: 'white'
    }
}


const App = ({classes}) => (
    <div className={classes.wrapper}>
        <h1 className={classes.title}>
            Hello JSS-React!
        </h1>
    </div>
)

export default injectSheet(styles)(App)

Glamorous

Glamorous

import React from 'react'
import glamorous from 'glamorous'

const Wrapper = glamorous.div({
    backgroundColor: 'black'
})

const Title = glamorous.h1({
    color: 'white'
})

const App = () => (
    <Wrapper>
        <Title> Hello JSS-React!</Title>
    </Wrapper>
)

export default App;

Radium (предупреждение: использует inline-стили)

Radium

import React, { Component } from 'react';
import Radium from 'radium';

@Radium // decorator
class App extends Component {
	render() {

        const styles = {
            wrapper: {
                background: 'blue',
            }
            title: {
                color: 'white'
            }
        };

		return (
            <div style={styles.wrapper}>
                <h1 style={styles.title}>Hello Radium!</h1>
            </div>
		);
	}
}

export default Radium(App);

Обратите внимание еще на то, что Radium использует декораторы.

Aphrodite

import React, { Component } from 'react';
import { StyleSheet, css } from 'aphrodite';

const styles = StyleSheet.create({
    wrapper: {
        backgroundColor: 'red'
    },
    title: {
        backgroundColor: 'blue'
    }
});

class App extends Component {
    render() {
        return (
            <div className={css(styles.wrapper)}>
                <h1 className={css(styles.title)}>Hello Aphrodite!<h1>
            </div>;
        )
    }
}

Stylotron

Stylotron

import React, { Component } from 'react';
import { styled } from 'styletron-react';

const Wrapper = styled('div', {
    backgroundColor: 'black'
})

const Title = styled('h1', {
    color: 'white'
})

class App extends Component {
    render() {
        return (
            <Wrapper>
                <Title>Hello Styletron!<Titleh1>
            </Wrapper>;
        )
    }
}

Это всё очень простые примеры для демонстрации основ работы. Все библиотеки имеют гораздо больше возможностей — например, использование тем, динамические входные параметры, рендеринг на стороне сервера и много чего еще!

Отличная статья, в деталях рассказывающая обо всех возможностях CSS-in-JS.

Вот полный список — вперед, попробуйте каждую из библиотек!

Нравится нам это или нет, но подход CSS-in-JS заслуживает того, чтобы его попробовать.

Убеждены, что CSS-in-JS не для вас? Тогда есть другой вариант — CSS модули!

Всем спасибо!

P.S. Это тоже может быть интересно:

6

Комментарии

  1. faiwer

    Гхм. В минусы я бы первым и самым жирным пунктом добавил, что мы заменяем конструкцию вида, скажем, createElement(‘div’, { …attrs }, children) на createElement(StyledClass, { …attrs }, children). Стало бы на каждый такой элемент мы заставляем React делать wrapper. Бррр. Столько оверхеда на ровном месте. Бррр. Или я не правильно понимаю принцип работы? Просто пока у вас маленькое приложение вам в принципе плевать, что и как делать. Даже замедлив его в 10 раз оно вроде будет шевелиться. Но мы же про SPA. Там в порядке вещей сложные вещи. Для примера какие-нибудь таблицы (а это сразу оочень много DOM). Если такое увлекаться wrapper-ами… в общем лучше не увлекаться.

  2. Алексей

    Из статьи не понятно будет ли CSS-in-JS для каждого компонента создавать style в head страницы.

    Если так то в минусы надо еще записать увеличение времени загрузки страницы. Насколько я понимаю каждый style заставить браузер перестраивать CSSOM учитывая новые найденные правила css.

    Так же ничего не сказано о отладке стилей — со всеми этими уникальными именами классов. Чтобы найти нужное свойство в исходном коде придется сначала понять что за компонент смотришь, найти его в js и т.д.?

    Есть ли поддержка редакторами кода? Т.е. использование автодополнения, emmet, других удобных инструментов.

    Модульные стили (как и вообще подход к разработке) это здорово. Но ИМХО лучше подождать (т.к. поддержка в браузерах пока слабая)и использовать для этого веб-компоненты. Или не ждать и использовать polymer.
    В компонентах есть все что описано в преимуществах CSS-in-JS (Компонентный подход, Селекторы с ограниченной областью видимости, В DOM находятся только те стили, которые на данный момент используются) и нет описанных выше недостатков.

    А вещи вроде подстановки браузерных префиксов или «использует всю мощь JavaScript для расширения возможностей CSS» ИМХО лучше делать с помощью postcss при сборке проекта.

  3. Николай Громов

    Как элегантно автор обошел вопрос использования пре- и постпроцессинга (префиксы — не всё) и чудовищный синтаксис написания таких стилей, однако :)

  4. Лев Солнцев

    Надо переиспользовать стили. CSS: добавляешь селектор через запятую, styled-components: копируешь всю портянку стилей, и надо не забывать дублировать все изменения кода. extend не предлагать — нужен другой тег.

    1. Сергей

      Вы не разобрались в работе styled-components. Например, есть метод withComponent, который это делает. Выносить общие стили и потом их переиспользовать никто не запрещает, про extend вы и сами написали. Более того import { css } from styled-components позволяет писать только css-код, а потом добавлять его внутрь какого угодно тэга. Т.е. ещё один вариант композиции стилей.

      Сам я вижу другую проблему в css-in-js. Мало какая технология позволяет эти стили выделить в css-файл на моменте сборке, чтобы их можно было держать где-то на cdn, облегчить генерацию стилей на клиенте

  5. Иван

    Нафиг это все, больше проблем чем решений. Для себя взял css модули (которые на самом деле scss)

Добавить комментарий для Николай Громов Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Ваш E-mail не будет опубликован

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.