Skip to content

Latest commit

 

History

History

kye-hohenberger-emotion

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 


Иллюстрация от https://twitter.com/ChrisMCarrasco

emotion. Следующее поколение CSS-in-JS

Перевод статьи Kye Hohenberger: emotion. The Next Generation of CSS-in-JS.

Emotion - это высокопроизводительная, легкая библиотека css-in-js. Основная идея исходит из библиотеки glam Сунила Пая, и её философия изложена здесь. Ключевая идея очень проста. Вам не нужно жертвовать производительностью ради удобства разработчиков при написании CSS. Emotion минимизируют стоимость исполнения css-in-js, анализируя ваши стили с помощью babel и PostCSS. Ядро библиотеки в рантайме занимает 2,3 кб и поддержка React — 4 кб. Всего.

Первый взгляд

API покажется вам знакомым, если вы знаете styled-components. Мы также черпали вдохновение в glamorous и css-modules.

const imageBase = css`
  width: 32px;
  height: 32px;
  border-radius: 50%;
`
const Avatar = styled.img`
  composes: ${imageBase};
  border: 2px solid ${p => p.theme.borderColor}

  @media(min-width: 420px) {
    width: 96px;
    height: 96px;
  }
`

Бенчмарки

Этот бенчмарк нагружает библиотеки, очень быстро изменяя динамическое значение в блоке стиля.

Против styled-components - emotion более чем в 25 раз быстрее при использовании высокодинамичных значений props и при этом меньше на треть.

Styled-components предупреждают об опасности использовании этого паттерна и вместо этого рекомендуют использовать инлайн-стили для высокодинамических значений. Emotion не имеют таких ограничений.

Против glamorous - emotion до 2,5 раз быстрее на ререндере и в половину меньше.

CSS

import { css } from 'emotion'

CSS это ❤️ emotion. Большая часть API, включая styled, является просто обёрткой над css.

const imageBase = css`
  width: 32px;
  height: 32px;
  border-radius: 50%;
`

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

css возвращает строковое имя класса, которое может использоваться для любого элемента. Для нашего блока стиля imageBase, описанного выше, это будет что-то вроде css-imageBase-12345.

Чтобы помочь с композицией, css принимает специальное свойство в блоке стиля, называемое composes.

С помощью composes мы можем использовать наш imageBase, чтобы быстро создавать стили аватаров.

const avatarStyle = css`
  composes: ${imageBase};
  border: 1px solid #7519E5
`

Внутренняя реализация emotion добавит эти css-классы к сгенерированному результату. avatarStyle получит имя класса вида css-imageBase-12345 css-avatarStyle-12345. Это позволяет создать действительно мощную композицию, которая особенно хорошо показывает себя вместе со styled.

const imageStyles = css({
  width: 96,
  height: 96
})

Объекты стилей не обрабатываются авто-префиксером.

styled

import styled from 'emotion/react'

styled это тонкая обертка вокруг css, которая поддерживает тот же стиль текста и выражений, что и css.

Использование styled очень похоже на использование styled-components.

const Avatar = styled.img`
  width: 32px;
  height: 32px;
  border-radius: 50%;
`

styled также работает как вызов функции. Первым аргументом может быть любой html-тег или React-компонент, который имеет поддержку свойства className.

const BigAvatar = styled(Avatar)`
  width: 96px;
  height: 96px;
`

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

const Heading = styled.img`
  font-family: serif;
`
const Header = styled.header`
  display: flex;
  ${Heading} {
    font-size: 48px;
    align-self: flex-end;
  }
`

Композиция также работает.

const imageBase = css`
  width: 32px;
  height: 32px;
  border-radius: 50%;
`
const Avatar = styled.img`
  composes: ${imageBase};

  @media(min-width: 420px) {
    width: 96px;
    height: 96px;
  }
`

В styled, значение интерполяции composes может быть функцией. Она вызывается с текущими props во время рендера. Эта функция должна возвращать className или объект стиля.

const types = {
  success: css`
    background: green;
  `,
  error: css`
    background: red;
  `,
}
const Alert = styled.div`
  composes: ${props => types[props.type]};
  padding: 10px;
`

Помните, что css просто возвращает className, так что это работает.

Истинная сила композиции раскрывается при её использовании с чем-то вроде styled-system Брента Джексона.

https://twitter.com/tkh44/status/883836708453855232/photo/1

Поддержка тем

import { ThemeProvider } from 'emotion/react'

Поддержка тем реализована с помощью библиотеки theming. Детали API подробно изложены по ссылке. Она основана на системе тем styled-components и мощно протестирована, что сделало её беспроблемной.

Всякий раз, когда вы предоставляете тему ThemeProvider, любой стилизованный компонент имеет доступ к этим стилям через props.theme. Неважно, как глубоко вложен ваш компонент внутри ThemeProvider, у вас все ещё есть доступ к props.theme.

const theme = {
  white: '#f8f9fa',
  purple: '#8c81d8',
  gold: '#ffd43b'
}
const Avatar = styled.img`
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid ${props => props.theme.gold}
`
<ThemeProvider theme={theme}>
  <Avatar src={avatarUrl}>
    hello world
  </Avatar>
</ThemeProvider>

Extract Mode против Inline Mode

Babel-плагин для emotion по умолчанию работает в так называемом «extract mode». В этом режиме мы берём все ваши css, определенные в каждом файле, извлекаем их в [filename].emotion.css и автоматически импортируем в верхнюю часть вашего JavaScript-файла. Динамические значения обрабатываются с помощью CSS-переменных. Единственные обновления во время выполнения - это просто изменения в CSS-переменных! Недостатки режима «extract mode» — отсутствие поддержки IE11 из-за CSS-переменных и невозможность извлечь критический CSS для рендеринга на стороне сервера.

В «inline mode» emotion работает несколько иначе.

В этом режиме используется CSS Object Model (CSSOM) для управления CSS из JavaScript. Так же работает любая другая библиотека css-in-js. Что отличает emotion, так это то, что мы можем прекомпилировать все ваши CSS-правила. Нам не нужно анализировать и дополнять ваши стили префиксами в рантайме, потому что мы уменьшаем ваши стили до непосредственных вызовов insertRule.

Самые большие преимущества режима «inline mode» заключаются в том, что он работает на IE9 и выше, и у него есть поддержка рендеринга на стороне сервера с помощью extractCritical. Вот отличный пример этого в репозитории next.js.


Под капотом

Следующий пример предназначен для режима «inline mode». Режим «extract mode» работает практически также.

Этот код

const H1 = styled.h1`
  font-size: 48px;
  color: ${props => props.color};
`

С помощью Babel компилируется в следующий

const H1 = styled(
  'h1',
  ['css-H1-duiy4a'], // generated class names
  [props => props.color], // dynamic values
  function createEmotionStyledRules (x0) {
    return [`.css-H1-duiy4a { font-size:48px; color:${x0} }`]
  }
)

Всякий раз, когда вызывается метод рендеринга H1:

  1. styled проходит через каждое динамическое значение в вашем блоке CSS. Когда он итерируется через этот список, любое значение, которое является функцией, вызывается и получает текущие props.
  2. createEmotionStyledRules вызывается с использованием конечных значений с шага 1 в качестве аргументов. В нашем примере результатом props => props.color будет x0.
  3. Результат этого вызова затем вставляется в обёртку StyleSheet emotions, где он агрессивно кэшируется.
  4. Сгенерированные имена классов добавляются к свойству className компонента.

Взгляните на исходники styled и css.

Бонус

CSS-анимации

Анимации поддерживаются с помощью функции keyframes.

Свойство css

Любой компонент в вашем приложении, который принимает свойство className, теперь может принимать свойство css.

const flexCenter = css`
  display: flex;
  align-items: center;
  justify-content: center;`

<div
  css={`
    composes: ${flexCenter};
    width: 128px;
    height: 128px;
    background-color: #8c81d8;
    border-radius: 4px;

    img {
      width: 96px;
      height: 96px;
      border-radius: 50%;
      transition: all 400ms ease-in-out;

      &:hover {
        transform: scale(1.2);
      }
    }
  `}
>

Свойство себя точно так же, как функция css. Полученное имя класса добавляется к свойству className элемента.


Пойдите, напишите немного CSS.

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

Огромное спасибо Митчеллу Гамильтону, потому что без него emotion не работали бы так хорошо. Его вклад был неоценим в том, чтобы мы достигли того, чего достигли.

Спасибо всем контрибьюторам, создателям ишью и тестировщикам ранних релизов!

emotion.sh — вебсайт

slack.emotion.sh — канал в Slack, заходите поздороваться! 👋

Спасибо, Nadim.


Слушайте наш подкаст в iTunes и SoundCloud, читайте нас на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.

Статья на Medium