Недавно компания Facebook* (aka Meta**) выпустила в опенсорс собственную CSS-in-JS библиотеку под названием StyleX. По заявлениям разработчиков, она отлично подходит для больших проектов и ключевым ее преимуществом является перфоманс.
В этой статье мы рассмотрим пример использования, основные функции и особенности данного решения.
Начать стоит с того, что StyleX – framework agnostic. Это означает, что библиотеку можно использовать с любым JS фреймворком, будь то React, Preact, Solid, Angular или Qwik. Однако те, что используют кастомные расширения файлов, типа Svelte и Vue, могут потребовать дополнительной настройки.
Предлагаю взглянуть на компонент Button, написанный на React и TypeScript:
import * as React from "react";
import * as stylex from "@stylexjs/stylex";
import type { StyleXStyles } from "@stylexjs/stylex/lib/StyleXTypes";
const MEDIA_MOBILE = "@media (max-width: 700px)" as const;
const ButtonStyles = stylex.create({
base: {
border: "none",
background: "none",
cursor: "pointer",
// Использование медиа-запросов
fontSize: {
default: "24px",
[MEDIA_MOBILE]: "16px",
},
},
primary: {
color: "white",
backgroundColor: "black",
},
secondary: {
color: "black",
backgroundColor: "white",
},
block: {
display: "flex",
width: "100%",
},
dynamic: (opacity) => ({
opacity,
}),
});
const Button = ({
color = "primary",
block = false,
styles,
}: {
color: string;
block: boolean;
styles?: StyleXStyles<{
backgroundColor?: string;
color?: string;
}>;
}) => {
return (
<button
{...stylex.props(
// Применяется по умолчанию
ButtonStyles.base,
// Применяется один из вариантов в зависимости от пропса color
ButtonStyles[color],
// Применяется условно в зависимости от пропса block
block && ButtonStyles.block,
// Применяется результат выполнения функции dynamic
ButtonStyles.dynamic(state.opacity),
// Стили, прокинутые пропсом
styles
)}
/>
);
};
Библиотека имеет довольно простое API, мы разберем основные функции и типы. С остальными можно ознакомиться по ссылке.
С помощью stylex.create()
создается объект стилей, свойства которого мы можем передать в stylex.props()
условно, по ключу или динамически – с использованием функции. Так как props()
возвращает объект, его нужно развернуть через spread-оператор.
А используя встроенный тип StyleXStyles
мы можем ограничить css-свойства, передаваемые извне.
В StyleX также доступно создание переменных, на которых можно строить дизайн-систему:
import * as stylex from '@stylexjs/stylex';
const colors = stylex.defineVars({
accent: 'blue',
background: 'white',
});
Обращение к ним происходит так же, как и к обычным свойствам объекта:
import * as stylex from '@stylexjs/stylex';
import {colors} from './vars.stylex.js';
const styles = stylex.create({
container: {
color: colors.accent,
backgroundColor: colors.background,
},
});
Ключевые преимущества
Разработчики библиотеки декларируют ряд достоинств:
Перфоманс – библиотека на этапе компиляции с помощью Babel-плагина превращает JS в оптимизированный по размеру единый CSS файл, таким образом мы избегаем классических CSS-in-JS проблем с рантаймом.
Масштабируемость – гениальный Babel-плагин использует кэширование на уровне файлов и генерирует атомарные названия классов, за счет чего минимизируется размер бандла, а рост количества компонентов в приложении не сильно отражается на размере CSS файла.
Предсказуемость – приоритизация селекторов в StyleX очень проста – всегда побеждает последний заданный стиль. Это значит, что можно забыть про каскадность.
Комбинируемость – объекты стилей можно экспортировать и передавать в компоненты. При этом стили всегда будут вести себя предсказуемо.
Типизация – каждое css-свойство и переменная полностью типизированы, а мы можем использовать TypeScript или Flow, чтобы определить стили, передаваемые в компонент.
Все в одном месте – библиотека поощряет определение стилей в том же файле, что и компонента. При таком подходе StyleX удаляет неиспользуемые свойства из бандла и убирает дубликаты.
История создания
Идея создать новую систему работы с CSS зародилась несколько лет назад, когда разработчики Meta начали переписывать фронтенд фейсбука на React.
Ранее в проекте использовалось нечто похожее на CSS-модули и lazy loading, но у этого подхода были проблемы с масштабированием. При навигации по сайту модули подгружались в разном порядке, что порождало различные казусы с приоритизацией стилей.
В StyleX высший приоритет всегда имеет последнее примененное свойство, а значит можно с легкостью контролировать стили и переопределять их при необходимости.
Еще одной проблемой стало огромное количество ненужных стилей. По заявлению разработчиков, раньше средний пользователь facebook загружал десятки Мегабайт CSS, большая часть которого даже не использовалась.
В результате перехода на StyleX размер первоначального CSS бандла facebook.com составил 140 Кбайт. На первый взгляд может показаться, что это много, но нужно учитывать, что эти стили покрывают 100% приложения. Браузер загружает их всего один раз, а потом достает кэша.
Конкуренты
По-моему, сравнивать StyleX, в первую очередь, стоит с Emotion и Tailwind.
Тут у нас, как и в Emotion, co-located подход – стили находятся в одном файле с компонентом. Для многих это большой плюс с точки зрения DX и читабельности кода. Но в то же время, StyleX обгоняет конкурента за счет отсутствия рантайм-нагрузки.
В сравнении с Tailwind, библиотека не так проста и удобна – мы не можем использовать сокращения из коробки, их придется создавать самостоятельно.
Зато нам доступны встроенные типы, которые позволяют четко определить свойства, которые можно будет переопределять. И для этого нам не нужно будет создавать отдельный пропс под каждое свойство.
Заключение
Думаю, данное решение будет полезно на больших проектах, при построении сложных дизайн-систем, которыми пользуются множество команд.
Компания Facebook* три года тестировала и дорабатывала свой продукт, прежде чем отдать его в опенсорс. Мне кажется, это достаточное основание полагать, что библиотека проверена и может использоваться в реальных проектах.
Лично я рад появлению этой технологии, а что думаете вы?
*Социальная сеть запрещена в России
**Компания Meta Platforms Inc. признана в России экстремистской организацией и запрещена.