Верней оформил 5 декабря 2023 года для всех то, чем его разработчики пользуются уже давно. Позиционируется данный помощник для js-подобных фреймворков типа React, Preact, Solid, lit-html и Angular, однако оперирующие html шаблонами Vue и Svelte тоже могут задействовать StyleX, но после предварительной специальной её кастомизации.
Код в React в итоге выглядит примерно так:
import * as stylex from '@stylexjs/stylex';
const colorStyles = stylex.create({
red: {
backgroundColor: 'lightred',
borderColor: 'darkred',
},
green: {
backgroundColor: 'lightgreen',
borderColor: 'darkgreen',
},
});
import * as React from 'react';
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({ ... });
function ReactDiv({ color, isActive, style }) {
return <div {...stylex.props(
styles.main,
// apply styles conditionally
isActive && styles.active,
// choose a style variant based on a prop
colorStyles[color],
// styles passed as props
style,
)} />;
}
StyleX компилируется в атомарные CSS, для чего используется Babel плагин.
Основная утверждаемая выгода от использования StyleX - переиспользование, типизация, будет всё круто на больших проектах. Ну и получение наконец полного "Single File Component" файла с js, css и html в одном флаконе.
Предполагается, что StyleX теперь будет продвигаться как best practice для React проектов.
Для сравнения DX вот примерно один и тот же код, написанный на React+StyleX и на Vue+CSS
Сравнить
React
import * as React from "react";
import * as stylex from "@stylexjs/stylex";
const colorStyles = stylex.create({
red: {
backgroundColor: "lightred",
borderColor: "darkred",
},
green: {
backgroundColor: "lightgreen",
borderColor: "darkgreen",
},
});
const styles = stylex.create({
main: {
padding: "1rem",
border: "1px solid grey",
},
});
function ReactDiv({ color, isActive, style }) {
return (
<div
{...stylex.props(
styles.main,
isActive && styles.active,
colorStyles[color],
style,
)}
/>
);
}
Vue
<script setup>
import { ref } from "vue";
const color = ref("red");
const props = defineProps(["style", "isActive"]);
</script>
<template>
<div
class="main"
:class="[color, { isActive: props.isActive }, props.style]"
/>
</template>
<style scoped>
.red {
background-color: "lightred";
border-color: "darkred";
}
.green {
background-color: "lightgreen";
border-color: "darkgreen";
}
.main {
padding: 1rem;
border: 1px solid grey;
}
</style>
Ну и интересны причины возникновения StyleX:
Предыдущий сайт Facebook использовал нечто похожее на CSS-модули и страдал от различных проблем, которые и вдохновили на создание CSS-in-JS. Средний посетитель facebook.com загружал десятки мегабайт CSS. Большая часть из них не использовалась. Чтобы оптимизировать первоначальную загрузку, мы лениво загружали наш CSS, что, в свою очередь, приводило к медленному обновлению (или "Interaction to Next Paint"). Использование сложных селекторов приводило к конфликтам или "войнам за специфичность". Инженеры часто прибегали к использованию !important или более сложных селекторов для решения своих проблем, что делало всю систему постепенно хуже.
Несколько лет назад, когда мы перестраивали facebook.com с нуля, используя React, мы поняли, что нам нужно что-то лучшее, и создали StyleX.
StyleX был разработан для масштабирования, и его дизайн доказал свою эффективность за годы использования. Мы добавили в StyleX новые функции без снижения производительности и масштабируемости, сделав StyleX еще более приятным в использовании.
Использование StyleX стало для нас в Meta значительным улучшением как в плане масштабируемости, так и в плане выразительности. На сайте facebook.com мы смогли сократить количество CSS-пакетов с десятков мегабайт лениво загружаемого CSS до одного пакета размером в пару сотен килобайт.
Мы создали StyleX не только для того, чтобы удовлетворить потребности React-разработчиков в стилях в Интернете, но и для унификации стилей для React в веб-версии и нативной версии.
Комментарии (22)
gun_dose
16.12.2023 07:35Выглядит точь в точь, как стили в react-native. И по своему опыту скажу, что это далеко не самое удобное решение, поэтому не вижу ничего хорошего в том, чтобы тянуть это в веб.
А вообще, десятки мегабайт стилей для Фейсбука - это какой-то нонсенс. Вот, что происходит, когда нанимают тысячу разработчиков, чтобы каждый из них покрасил одну из тысячи одинаковых кнопок. Вместо того, чтобы взять несколько толковых людей и построить простую и понятную дизайн-систему.
themen2
16.12.2023 07:35Почему не очень хорошие. Можете раскрыть?
gun_dose
16.12.2023 07:35Потому что если при обычном css я хочу сделать, скажем, текстовый блок с рамочкой в разных местах, то достаточно просто присвоить элементу нужный класс. А при использовании подобных библиотек нужно будет вместо класса присваивать объект стилей. И там, где CSS подразумевает банальное использование нескольких классов, теперь придётся мерджить один объект стилей из нескольких. Это мало того, что неудобно для разработчика, так ещё и раздувает результирующие стили, снижая производительность веб приложения.
И если для react-native это приемлемо, потому что это не веб-платформа, то в вебе это всё нужно, как собаке пятая нога.
newervi
16.12.2023 07:35только не рассказывайте, что можно использовать 1 .scss файлик назвав его global.scss (а можно ещё даже его разбить на 10 составных файлов подгружая всё в него (модульность уф)) и подгружать его первым всегда, чтобы стили были глобальные, которые они переиспользуют в каждом компоненте, тогда не придётся в каждом компоненте вызывать заново их JS код и писать htmlcss через JS код, что, конечно же, сокращает вес CSS файлов, но не уверен, что сокращает вес JS файлов)
*небольшой эдит для уточнения - подгружать файл можно 1 раз в index.hml или в +layout.svelte каком-нибудь или ещё где, его легко можно сделать "глобальным" уверен в каждом фреймворкеgun_dose
16.12.2023 07:35Современные фронтендеры никогда не пойдут на то, чтобы вот так просто загрузить файл стилей, а в компоненте просто использовать класс, к которому привяжутся стили. Это же придётся признать, что все эти 100500 библиотек стилей просто не нужны и как минимум половину кода проекта можно будет просто удалить за ненадобностью.
Кстати, многие в погоне за сокращением размера файла стилей забывают о таком понятии, как браузерный кэш, поэтому иногда лучше иметь один файл в 100кб, чем десять или даже пять по 10кб.
newervi
16.12.2023 07:35я вообще не понимаю как в серьёзном проекте, а не в тестовом, можно использовать библиотеки стилей. В моём понимании все эти библиотеки существуют только для того, чтобы их можно было за 1 минуту подключить и создать демку не тратя времени на стили... Очевидно, продумывая структуру сайта - нужно продумывать структуру сайта, а не перезаписывать стили из 10 разных библиотек в каждом компоненте....
gun_dose
16.12.2023 07:35Это не библиотека стилей, а библиотека, чтобы писать стили на javascript. Ничего перезаписывать там не приходится. Но в данном случае проблема в том, что повторно использовать что-либо будет либо неудобно, либо неэффективно с точки зрения производительности
Semigradsky
16.12.2023 07:35Это же JS, из одного файла экспортнули, в другой импортировали. Не понятно какие могут быть проблемы с переиспользованием.
gun_dose
16.12.2023 07:35Если стиль нужно использовать точно такой же, то проблем нет. А если в один компонент нужно применить стили из двух, то нужно из двух объектов клеить один и присваивать его. А на выходе это скомпилируется в набор CSS-правил, которые уже описаны в двух других местах. То есть итоговое количество стилей увеличится.
Dertamio
16.12.2023 07:35а можно поподробней, почему лучше иметь один файл в 100кб, чем десять по 10кб?
gun_dose
16.12.2023 07:35Тут суть не в арифметике, а в логике работы веб-приложений. Само собой, что десять маленьких файлов - это модули, которые будут грузиться не на одной странице, а на разных. И пользователь при переходе с одной страницы на другие будет догружать необходимые стили. А если всё это упаковать в один единственный файл, то он загрузится только один раз, а при переходе по внутренним страницам будет браться из кэша, что в итоге сильно снизит количество сетевых запросов, ускорит отрисовку самих страниц плюс снимет нагрузку с сервера.
Естественно,что при общем объёме стилей в 10МБ это не сработает, но истина где-то по середине: стили можно дробить, но не слишком мелко. Условно говоря, если в файле всего 10 строчек, то время, затраченное на его загрузку по большей части будет состоять из времени на постановку запроса в очередь и установку соединения, и если его разделить на два файла по 5 строк, то эти два файла суммарно будут грузиться дольше.
И ещё один очень забавный момент: у CSS-чанков обычно хэшированные имена, лежат они в папке с длинным именем и могут иметь гет-параметры. Так вот, иногда длина URL CSS-файла может оказаться больше, чем его содержимое. То есть, если в HTML-документ вместо URL сразу инлайново запихнуть содержимое чанка, размер HTML уменьшится.
Semigradsky
16.12.2023 07:35Есть библиотеки развивающие этот подход, Atomic CSS и многочисленные вариации.
nin-jin
16.12.2023 07:35Примечательно, что "типизация" не помешала вам использовать в примере не существующий стиль "active" и несуществующий цвет "lightred". А теперь сравните с $mol_style, где мало того, что поддерживается каскад без проблем со специфичностью, так он ещё и тайпчекается:
- \Объявляем компонент $my_div $mol_view attr * my_div_color <= color \ my_div_active <= active false sub / <= message \ - \Используем его в другом компоненте $my_app $mol_page body / <= Message $my_div color \green active true message \Hello!
namespace $.$$ { // Добавляем стили к компоненту $mol_style_define( $my_div, { padding: `1rem`, color: `black`, border: { width: `1px`, style: `solid`, color: `grey`, }, // Переопределяем стили в зависиимости от атрибутов '@my_div_color': { red: { background: { color: `indianred` }, border: { color: `darkred` }, }, green: { background: { color: `lightgreen` }, border: { color: `darkgreen` }, }, }, '@my_div_active': { true: { border: { width: `2px` }, } }, } ) $mol_style_define( $my_div_app, { width: `100%`, height: `100%`, // Перебиваем стили для всех инстансов компонента внутри $my_div: { border: { style: `dotted`, }, }, // Перебиваем стили для конкретного инстанса компонента внутри Message: { '@my_div_active': { true: { border: { width: `5px` }, }, }, }, } ) }
MyraJKee
16.12.2023 07:35Десятки мегабайт css, они там даже !important использовали. В чем смысл жёсткого отбора, если потом получается как у всех.
Per_Ardua
16.12.2023 07:35Ого! Фэйсбук придумали свой styled-components, только с нотацией покорявее, и с пока что неизвестными приемуществами.
Ну, молодцы, что тут ещё сказать...
LabEG
Фейсбук видимо решил прислушаться к моей статье, несмотря на то что любители css мне слили карму
https://habr.com/ru/articles/707510/
* сарказм
Scoo909010
Так в той статье тебе слили карму за то, что не привел достаточно обоснованных аргументов