CSS-live.ru

Всё, что вам нужно знать о 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. Это тоже может быть интересно:

11 комментариев

  1. Гхм. В минусы я бы первым и самым жирным пунктом добавил, что мы заменяем конструкцию вида, скажем, 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 при сборке проекта.

    1. Я работал только с jss, имена классов в DOM выглядят так: Example-root-1  — где Example — название компонента, а root название класса. И если ты пишешь на React, то в принципе у тебя в папке с компонентом лежат компонент и стили, с отладкой ничего сложного.

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

    1. Синтаксис написания достаточно прост, не обязательно jss стили писать в файле с основным компонентом, просто выносишь в отдельный файл и описываешь все стили там в одном объекте, это похоже на обычный css:

      const styles = {

      root: {

      backgdoundColor: ‘red’,

      fontSize: ’12px’

      },

      someStyle: {

      color: ‘#FFFFFF’,

      border: ‘1px solid black’,

         ‘&:hover’: {
           background:’none’
        }

      },

      }

       

      Просто описываешь стили в виде объекта в отдельном файле и импортишь его в компонент.

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

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

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

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

  6. В месте «Как работают inline-стили. В браузере это добавится к DOM-узлу таким образом: …» потерялись картинки

    1. Там были Gist-ы с кодом, которые отвалились (в оригинале статьи тоже), веб-архив не успел их сохранить:(

Оставить комментарий

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

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