CSS-in-JS и React Server Components
Почему некоторые решения CSS-in-JavaScript не могут работать в новом серверном мире React — и что с этим делать.
Введение
С эволюцией экосистемы React, особенно с переходом Next.js к подходу "server-first", приложения, использующие решения CSS-in-JS, такие как Emotion и styled-components, сталкиваются с серьезными проблемами. Эти библиотеки - CSS-in-JS по своей природе несовместимы с новой парадигмой React.
Хотя можно предположить, что эти библиотеки могут быть доработаны для соответствия новому подходу, важно помнить, что они поддерживаются волонтерами и ждать от них полной переработки, чтобы адаптировать их к изменениям в React — это сложная и, возможно, нереалистичная задача.
Давайте рассмотрим, почему некоторые решения CSS-in-JS не могут работать в новом серверном мире, а затем познакомимся с библотекой CSS-in-JS - Linaria, которая использует практически идентичный со styled-components API.
Парадигма RSC
Для начала важно понять, как работал React до появления Server Components (до RSC) и как он работает сейчас (после RSC).
До RSC: как на сервере, так и на клиенте
До RSC, React рендерил компонент как на сервере, так и на клиенте. React всегда рендерил статические части HTML-страницы на сервере и отправлял их клиенту для "гидратации". Этап гидратации — это клиентский JavaScript, который отвечает за работу с ссылками или прикрепление обработчиков событий, что позволяет обеспечить интерактивность.
После RSC: только на сервере
Однако теперь после RSC все компоненты React по умолчанию являются серверными. React рендерит статические части HTML-страницы, но не гидратирует их на клиенте, что снижает/устраняет любой клиентский JavaScript. Это интересный подход, так как после RSC мы можем делать серверные запросы из любого компонента на любом уровне дерева, а не только на уровне маршрута, как это было ранее в Next.js.
Новая директива React Use Client
Вы все еще можете включить клиентский JavaScript и методы React, такие как useState, useEffect и т.д., но вы должны явно добавить директиву "use client" в начало файла, чтобы React вел себя как обычно.
Почему CSS-in-JS не работает с RSC?
Для понимания этого вопроса важно понимать, как работают "устаревшие" библиотеки CSS-in-JS.
Такие библиотеки, как Emotion и styled-components, выполняют большую часть работы в режиме реального времени на клиенте с использованием клиентского JavaScript, который, как мы установили, больше не существует в RSC, или "серверных" компонентах.
Если вы видели приложение, использующее styled-components, вы, вероятно, замечали странные названия классов.
Это происходит потому, что со styled-components вы не пишете CSS-классы, вы пишете компоненты, библиотека выполняет извлечение стилей, преобразование их в CSS, который понимает браузер, и генерирует случайное, но уникальное имя класса, а затем применяет его к DOM-элементу для их связывания.
Все не так плохо
Существуют новые решения этой проблемы, которые перемещают основную работу с клиента на этап сборки. Мы конвертируем TypeScript в "браузерный JS" на этапе сборки поэтому выполнение CSS-in-JS на этом этапе кажется отличным решением для RSC.
Однако здесь сразу возникает логичный вопрос: не придется ли разработчикам по всему миру переписывать все свои объявления styled(точка) с использованием новомодного API? Согласитесь, звучит жутковато.
К счастью, создатели Linaria, которая существует с 2017 года, выбрали очень похожий API на styled-components. Pigment CSS от команды MUI последовал аналогичным путем.
Как работают библиотеки CSS-in-JS, совместимые с RSC?
В случае с Linaria она использует CSS-модули. Если использовать пример styled.a:
Вместо создания случайного, но уникального имени класса, Linaria создает файл [component name].module.css и преобразует объявление styled в реальный CSS-класс, например:
На этапе сборки вашего приложения импорт в CSS-модуль .css будет добавлен в компонент, и объявление styled будет преобразовано в основной HTML-элемент (в данном случае это элемент якоря), и будет добавлена ссылка на класс:
Поскольку все это происходит на этапе сборки, браузеру ничего не нужно делать — и, следовательно, не требуется клиентский JavaScript. Это действительно отличное решение проблемы, и все, что нужно для миграции, это удалить styled-components, установить Linaria и выполнить поиск и замену с
на
Итог
Когда что-то, столь широко используемое, как React, полностью меняет свой основной принцип работы, многие проекты с открытым исходным кодом, разработанные для него, также должны измениться — или, в некоторых случаях, остаться в истории. В мире JavaScript все меняется часто, и все, что мы можем сделать, это стараться не отставать!