Действительно классное новое исследование CSS-in-JS

Действительно классное новое исследование CSS-in-JS

От автора: сегодня мы рассмотрим Compiled и его пакет @compiled/css-in-js. Он предназначен для того, чтобы предоставить знакомый опыт создания CSS-in-JS без затрат времени выполнения.

Этот пример представлен на веб-сайте Compiled:

import { styled } from '@compiled/css-in-js'; export const ColoredText = styled.span` color: #ff5630;
`;

Во время установки Compiled вы добавляете плагин Babel, который называется @compiled/babel-plugin-css-in-js. Когда вы создаете проект, этот плагин преобразует код, который вы видели выше, в следующее:

import React from 'react';
import { Style } from '@compiled/css-in-js'; export const ColoredText = React.forwardRef( ({ as: C = 'span', ...props }, ref) => ( <> <Style hash="css-zd46j1">{['.css-zd46j1{color:#ff5630;}']}</Style> <C {...props} ref={ref} className={ 'css-zd46j1' + (props.className ? ' ' + props.className : '') } /> </> )
);

Преобразование изменяет в коде импорт styled на импорт Style. Задача компонента Style — перенести стили в head приложения. Style — это крошечный компонент. Вы можете увидеть его реализацию здесь. Этот компонент является единственной частью Complied, которая остается в коде после преобразования, поскольку она необходима во время выполнения.

Компонент Style получает свойство hash, которое используется, чтобы обойтись без необходимости добавления правил , если они уже были добавлены. Значение hash — это хэш правил CSS в сочетании, которые были добавлены к этому компоненту. Но мы не беспокоимся об этом. Давайте заглянем под капот.

Свойство css

Свойство css, получившее известность в Emotion также доступно в Compiled. Отсутствие необходимости придумывать имена для промежуточных компонентов — одна из вещей, которые мне больше всего нравятся в использовании свойства css.

import React from 'react';
import '@compiled/css-in-js'; export const EmphasisText = (props) => { const color = props.massive ? '#00B8D9' : '#36B37E'; return ( <span css={{ color, textTransform: 'uppercase', fontWeight: 600, }}> {props.children} </span> );
};

Compiled преобразует это в:

import React from 'react';
import { Style } from '@compiled/css-in-js'; export const EmphasisText = (props) => { const color = props.massive ? '#00B8D9' : '#36B37E'; return ( <> <Style hash="css-vmwvfg"> {[ '.css-vmwvfg{color:var(--var-1ylxx6h);text-transform:uppercase;font-weight:600;}', ]} </Style> <span className="css-vmwvfg" style={{ '--var-1ylxx6h': color }}> {props.children} </span> </> );
};

Обратите внимание, что мы получаем тот же компонент Style, с которым мы уже знакомы. Все в Compiled сводится к использованию этого компонента во время компиляции.

Вы могли заметить, что мы используем здесь динамический цвет. Динамическая часть превращается в переменную CSS (color:var(—var-1ylxx6h)). Значение переменной css будет передано в spanas style={{ ‘—var-1ylxx6h’: color }}.

Помимо объектно-ориентированного API, свойство css также может принимать строки, и результат будет таким же.

<span css={` color: #00b8d9; text-transform: uppercase; font-weight: 600; `}
> {props.children}
</span>

Передача класса и style

При передаче либо className, либо style вам необходимо статически определить их. Так как Compiled не может статически знать, где будет находиться какой-либо объект, если мы его распространяем, это нужно делать явно. (Документация)

Следующий пример будет работать, поскольку и style, и className можно анализировать статически:

<div style={{ display: 'block' }} className="my class name" css={{ fontSize: 12 }}
/>

Однако этот пример не будет работать, предполагая, что props содержит стили или имена классов.

<div {...props} css={{ fontSize: 12 }} />

Генерация CSS во время выполнения

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

import styled from 'styled-components'; const getBackgroundGradient = (color, percent) => ({ `linear-gradient(${color}, ${color}) 0 / ${percent}% 100% no-repeat ${color}`,
}); styled.input` background: ${props => getBackgroundGradient(props.color, props.percent)};
`;

Заметьте, что только значение является динамическим, но background определяется статически. Это то, что позволяет Compiled затем использовать переменные CSS для установки значения соответствующим образом. Согласно документации, это повышает производительность, так как CSS не нужно менять во время выполнения.

Объединение

Когда несколько стилей определяют одни и те же свойства, стиль, который применяется последним, всегда имеет приоритет.

import React from 'react';
import '@compiled/css-in-js'; const danger = { color: 'red',
}; const base = { color: 'hotpink', padding: '0.5rem 0', backgroundColor: 'rgba(0, 0, 0, 0.05)',
}; export const CompositionOrder = () => { return ( <div> <div css={base}>This is hot pink.</div> <div css={[danger, base]}>This is also hot pink.</div> <div css={{ ...base, ...danger }}>This is red!</div> </div> );
};

В этом примере мы видим, что [danger, base] сделаем цвет ярко-розовым. Когда объекты типа применяются в обратном порядке, красный цвет будет иметь приоритет: { …base, …danger }. Это приводит к предсказуемому результату при объединении стилей. Подробности описаны в документации.

Рендеринг на стороне сервера

Compiled поддерживает рендеринг на стороне сервера из коробки. Вы можете просто импортировать компонент Complied, и он будет работать. Как мы видели в первом примере, стили окажутся рядом с самим элементом. Это замечательно, поскольку вы можете использовать потоковый API (renderToNodeStream) для потоковой передачи компонентов вместе со связанной разметкой, что приводит к более быстрой визуализации объектов.

Поддержка TypeScript

Поскольку Complied поддерживает TypeScript, вы можете использовать обобщения для описания любых свойств, которые могут понадобиться компоненту. Пример из документации:

import { styled } from '@compiled/css-in-js'; const BigIfTrue = styled.div<{ big: true }>` font-size: ${props => big ? 100 : 10};
`; <BigIfTrue /> ^^^^^^^^^ Property 'big' is missing in type [..]

Здесь универсальный styled.div<{ big: true }> используется, чтобы сообщить TypeScript, что компоненту BigIfTrue требуется логическое свойство big.

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

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

Краткое резюме

Давайте вспомним то, что мы рассмотрели до сих пор.

Свойство css

import '@compiled/css-in-js'; <div css={{ fontSize: 12 }} />;

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

import { styled } from '@compiled/css-in-js'; styled.div` font-size: 12px;
`;

Я нашел упоминание о некоторых нерешенных проблемах, таких как сбой селектора nth-child из-за вставленных тегов стилей (для которых есть RFC), или шаблон переопределений, не имеющий полных возможностей, которые он мог бы иметь в других библиотеках. Но на самом деле это может быть хорошо, так как предоставляет более специфичный API.

Помимо этих небольших нерешенных вопросов, Compiled выглядит захватывающим проектом, который стоит оценить. Я не упоминал об этом в этой статье, но Compiled также поддерживает имена классов, имеет специальный Jest матчер toHaveCompiledCss для тестирования, поддерживает Composing components, и может быть использован с компилятором TypeScript. Авторы даже работают над преобразователем, который извлекает CSS, тем самым полностью удаляя среду выполнения, поэтому звездочка (*) в подзаголовке может вскоре оказаться ненужной!

Автор: Dominik Ferber

Источник: https://medium.com

Редакция: Команда webformyself.