common issue example for css-in-js
common issue example for css-in-js

Пролог

Технология css-in-js существует уже довольно давно. Ещё в начале своего профессионального опыта я встречал подходы, в которых стайлинг локальных частей интерфейса пробрасывался в html через javascript в виде css директив. Иногда это необходимая мера, хотя необходимой она случается изредка, но раз в год, как говорится, и палка стреляет. У меня на опыте был пример построения раздела интерфейса, в котором устанавливаемое на сайт пользователя модальное окно можно рестайлить через кодовый редактор с live preview. css-in-js бывает оправдан, поэтому хочу сразу оговориться - хоронить никакой подход не стоит. Но и идеализировать его как универсальную пилюлю тоже не надо. Рендер стилей, привязанный к логике рендера компонентов в контексте всего проекта - это просто свой путь со своими приключениями, появившийся на мой взгляд в общей психопатии привязывать к state всё что только можно. Что если посмотреть - откуда взялась эта техногогия? На сегодняшний день на рынке проектирования интерфейсов сложился монополист react, диктующий программистам свои правила игры, и который даже без использования styled модуля имеет в себе простейщую инверсию управления cssInJs.
React - важный персонаж в этой теме. Он, словно useEffect всего современного front-end - стал центром силы, средоточием зла в виде голого state management, не предусматривающего из коробки ничего иного.

Редкий кадр - Шина событий под React. 
Ничего подобного в каком-то более менее продуманном решении в npm я не нашёл.
Редкий кадр - Шина событий под React. Ничего подобного в каком-то более менее продуманном решении в npm я не нашёл.

А что там у остальных?
Возьмём ближайшего к react лидера серьёзного интерпрайза - angular. Их разработчики по большей части до сих пор предпочитают использовать sass с голыми методологиями (SMACSS/BEM) и для них cssInJs и передача управления значениями стилей остаётся на уровне экспериментов.

Свои цифры производительности

Проводить свои опыты по измерению скорости инициализации бандла и исполнения его отдельных функций я в этой статье не стану, во-первых потому что мне лень, а во-вторых потому что это уже сделали за меня. С последней ссылкой очень рекомендую ознакомиться всем разработчикам, хайпящим на syled словно на мане небесной.

вес бандла сократил? мммолодец!
вес бандла сократил? мммолодец!

На самом деле толком и измерять ничего не надо чтобы понять, что стайлинг, плотно вплетённый в react займёт часть памяти в VirtualDOM и отнимет часть процессорного времени на все хуки жизненного цикла для обновления своих директив согласно актуальному state. Тут как раз таки cssInJs противопоставляет себя precompiled css, при чём не в лучщем свете. Ведь одно дело если реакт отрендерил в элемент класс и уже лежащие в памяти стили привязались к элементу, а совсем другое, если реакт отрендерил стили элемента, отрендерил класс, браузер инициировал стили и стили привязались к элементу.

Производительность бандла, падающего пользователю в браузер - не единственные страдающие цифры. Скоростью сборки и обновления в рантайме разработки тоже приходится жертвовать, особенно если в связке со styled используется typescript (за исключением последней версии без polling подхода при перепроверке кода).

Моё отношение к styled

Для начала хочу отметить в styled модуле наличие createGlobalStyles. И оно немудрено - одними локальными стилями компонентов сыт не будешь. А если учесть что стили плотно привязаны к жизненному циклу компонентов - держать в них глобальные стили - плохая идея. Приведённая выше функция решает эту проблему, но всё таки выбивается из композиции из-за чего лично мной воспринимается как костыль.

Выводы

Если вас сильно раздражают выбивающиеся из общей картины стилей необходимые вставки директив из javascript, к примеру если цвета с сервера приходят и применяются на элементы, итп. И у вас в проекте такого веселья много - styled для таки проектов подходит идеально. Если же в проекте весь стайлинг изначально известен, нет никаких подобных извращений - я выберу SASS.

Но по какому бы пути я ни пошёл - дай мне только волю - я и туда и сюда вкручу tailwind =)

Комментарии (23)


  1. DmitryKazakov8
    21.01.2023 20:02
    +9

    Однозначно палец вверх за статью, хороший заход из песочницы, но перфоманс в широком смысле - это меньшая из зол css-in-js. Можно вполне жить и с увеличенным временем сборки, и с тормозами в интерфейсе, и замедленной работой IDE и TS за счет новых ts-файлов. И даже с разметкой таблицы, где в style в каждой колонке вшиты стили и при SSR размер html-страницы за счет этого вырастает в разы, а найти элемент в разметке инспектора по стилям и потом в проекте - отдельная форма извращения, в том числе если эти стили автоматом выносятся в классы с md5-like наименованием. А даже с фичей вынесения классов редко какой css-in-js движок делает достойную оптимизацию типа CSSO, и не все умеют в полифиллы по browserslist.

    Недостатков в dev experience намного больше. Начиная от отсутствия поддержки подсветки синтаксиса и автоформатирования внутри js-строк, продираясь через тонну компонентов Button / StyledButton, по которым не поймешь - стиль это или компонент, потом пролезая через море оберток в инспекторе Components из React Dev Tools и постоянной работы с omitProps, чтобы этот стилизованный компонент прокидывал все что нужно в собственно целевой реакт-компонент и TS не ругался, приходишь в итоге к "> * {}" для стилизации children. И можно считать себя счастливчиком, если при этом всем не пришлось допиливать функционал, который есть в любом препроцессоре из коробки...

    Мне сложно понять, кто может использовать эту технологию в production, но почему-то статьи на хабре в пользу css-in-js до сих пор появляются, хотя их уже мало кто комментирует - слишком много уже это все обсуждали.


    1. petrov_engineer
      21.01.2023 21:16
      +1

      Не согласен ни с статьей, ни с вашим комментарием, достаточно просто загуглить, вот, например, расширение для VS Code с подсветкой и, более того, с автодополнением.
      Инлайновые стили вообще рудимент, разве что для email верстки осталось.
      К тому же, есть отличные решения https://linaria.dev/ и https://vanilla-extract.style/, которые вообще извлекают ваши стили в обычные CSS чанки.
      В классическом styled-components, чтобы писать стили с переопределением для потомков, достаточно передавать компонент, что описано прямо в документации

      Что касается статьи, "common issue example" якрий пример, что просто не разобрались как настроить бандлер и правильно подключить, хотя там тоже ничего сложно нет.

      Шина событий в React -- это вообще что-то с чем-то, естественно если так делать ничего на npm и не найдете, просто говоря, PubSub можно организовать как тут в mitt, но все React компоненты объявляются в обычных модулях, и что мешает просто импортировать созданный bus? Зачем оборачивать в контекст? Одни вопросы...


      1. DmitryKazakov8
        21.01.2023 21:32
        +2

        Подсветка, автодополнение - да, сейчас решаются плагинами. Автоформатирование stylelint, проверка синтаксиса - их тоже уже подвезли? Если так, и для всех распространенных IDE + вариант через консольную команду для precommit и CI, то хорошо, этот пункт снимается.

        Указав на пример styled(Button) вы проигнорировали пункт про omitProps - какие пропы должны передаваться в стили, а какие в компонент? А если в компоненте стоит { onClick, ...otherProps } = this.props, и вы забыли обернуть styled(Button) в omitProps(styled(Button), 'someStyleProp'), то будет ругань в TS, в консоли от неизвестного пропа для html + поломка гидрации SSR в ряде случаев. Очень много с этим сталкивался, под полсотни hotfix в master у меня из-за этого накопилось в тех проектах, которые использовали css-in-js.

        Про извлечение в чанки с автосгенерированным именем тоже описал ряд консернов.

        Зачем с этим всем бороться на пустом месте, когда альтернатива в виде CSS Modules лишена всех этих недостатков?


        1. petrov_engineer
          21.01.2023 21:40
          -1

          То что вы забили, что у вас в props передается параметр для стилизации -- чисто ваша проблема, уж извините, но и в случае с CSS Modules будет тоже самое.

          CSS-in-JS -- это просто другой подход, да и не одним styled-components едины, как и писать просто CSS стили, если лично вам не нравится -- не используйте, вас никто не заставляет.


        1. petrov_engineer
          21.01.2023 21:49

          Для stylelint кстати есть официальный плагин, не понимаю в чем проблема


          1. DmitryKazakov8
            21.01.2023 22:02
            +2

            Проблем намного больше, это просто верхушка айсберга - я написал комментарий в поддержку статьи, так как сам столкнулся с огромным количеством проблем от разных библиотек css-in-js и полностью разочаровался в этом подходе, который только через несколько лет начал подбираться по удобству к написанию стилей в отдельных файлах с препроцессором. И все мои коллеги согласны с этим, все без исключения вернулись к CSS modules - поэтому IMHO css-in-js может использоваться только теми, кто недостаточно с ним поработал. Часто я говорю сторонникам "через какое-то время вы со мной согласитесь", и это оправдывалось в ряде случаев. Я вас понимаю, сам был оптимистичен и использовал многие подходы из вшивания стилей в разметку.

            В CSS Modules нет проблемы передачи пропов стилизации. Есть компоненты, есть стили, которые прикрепляются через className - это единственная точка соприкосновения стилей и компонентов. Нет возни с TS, нет лишних оберток и компонентов, нет лишних импортов, нет лишнего кода в бандле, нет дополнительной ответственности разработчика, нет дополнительного времени на реализацию.


            1. petrov_engineer
              21.01.2023 22:51

              Для начала хорошо бы привести пример из "глубин", но почему-то вы приводите невладение технологией как контраргумент ее использования. Не поймите меня неправильно, я тоже использую CSS Modules и всеми руками за простоту и меньший размер бандла, в сравнении с решениями CSS-in-JS, но возбранять этот подход не собираюсь.


              1. DmitryKazakov8
                21.01.2023 23:10
                +4

                Эту технологию очень легко испортить - и то, что я писал из поверхностного, встречалось в каждом проекте, где использовалась css-in-js. И отсутствие корректного управления пропами, и стилизация чилдренов через звездочку, и отсутствие автоформатирования, и непонятные класснеймы либо инлайновые стили, и раздутое дерево компонентов, и смешение компонентов и стилевых компонентов, и деградация перфоманса. Если можно сделать столько ошибок в технологии - то вряд ли ее можно рекомендовать. Если для настройки и создания требований требуется опытный в этом senior - то популяризацией заниматься не нужно, кто умеет приготовить и проконтролировать корректное использование на протяжении всего цикла жизни проекта - тот будет использовать. Но статьи на хабре часто берут на вооружение молодые специалисты, и пока они соберут все грабли - проект может зарыться в багах и низкой скорости разработки. А "готовить" css-in-js очень сложно, требуется постоянный контроль, с этим вы скорее всего согласитесь


                1. petrov_engineer
                  21.01.2023 23:22
                  -2

                  Вы опять перекладываете ваши психологические травмы от неправильного использования технологии на саму технологию.

                  Кажется мы ходим по кругу, все что вы написали мы уже решили

                  /thread


                  1. funca
                    21.01.2023 23:49
                    +4

                    Вы опять перекладываете ваши психологические травмы

                    Не, вы так ни кого не убедите - нет одного технического аргумента. :) На самом деле, будет лучше, если покажите пример удачного использования css-in-js. Ссылу на git такого проекта, например.


                    1. petrov_engineer
                      21.01.2023 23:54
                      -3

                      У меня нет цели кого-либо в чем-либо убеждать, достаточно зайти на главную страницу и посмотреть какие компании используют: spotify, airbnb, tinder, patreon и т.д.


                      1. funca
                        22.01.2023 00:58
                        +3

                        Интересне увидеть что-то более практическое, чем отсылку к авторитетам. Вы же инженер.

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


                      1. markelov69
                        22.01.2023 11:40
                        +1

                        и посмотреть какие компании используют: spotify, airbnb, tinder, patreon и т.д.

                        Хахахаха, вот это логика "настоящего программиста". А если завтра spotify, airbnb, tinder, patreon и т.д будут делать себе операцию по смене пола, вы тоже сделаете что-ли?

                        Куда катиться мир, куда катится профессия...


      1. p07a1330
        21.01.2023 22:17
        +1

        Инлайновые стили вообще рудимент

        Не совсем. Штука своеобразная, но лично я ее использую на текущем проекте.

        Применимы в 2х случаях.

        Используем их если нужно встроить пользовательский html - применяется такой подход, что пользователь помимо всего процего на страничке имеет области, в которые может встроить баннеры. Туда инлайнится простой html через dangerousSetInnerHtml - в котором в том числе есть инлайновые стили. Так мы уверены, что коллизии имен не произойдет и пользовательские стили точно корректно перезапишут все, что ему нужно.

        Второй кейс из практики - нужно расположить точку на круге в зависимости от параметра (условно угла). Соответсвенно, смещение от центра расчитывается простенькой формулой через синус/косинус и подставляется в transform: translate


        1. petrov_engineer
          21.01.2023 23:00

          Я имел ввиду в контексте CSS-in-JS решений, но не суть важно.

          Пользовательский html, да и ещё с пользовательскими стилями штука достаточно, как минимум, странная, не говоря об XSS уязвимостях, легко привести пример когда без обработки этим можно выстрелить себе в ногу

          <div style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;"></div>

          Второй кейс имеет право на жизнь, но спокойно покрывается CSS custom variables, которые современные CSS-in-JS и используют


          1. p07a1330
            21.01.2023 23:54

            Пользовательский html

            При добавлении пользовательского контента он проходит премодерацию
            Так что такой вариант предусмотрен

            С custom variables думали делать, но там получалось как минимум не проще, чем в подходе описанном выше. Плюс для получения "аналогового" решения нужно слишком много генерированных стилей


    1. nin-jin
      22.01.2023 01:32
      -2

      Я просто оставлю это здесь: https://github.com/nin-jin/slides/tree/master/css-in-ts

      $mol_style_define( $my_profile , {
      	Details: {
      		Body: {
      			overflow: 'overlay',
      			$mol_button: {
      				border: {
      					radius: rem(.5),
      				},
      			},
      		},
      	},
      } )

      ????

      [my_profile_details_body] {
      	overflow: overlay;
      }
      
      [my_profile_details_body] [mol_button] {
      	border-radius: .5rem;
      }


  1. Dartess
    21.01.2023 23:15
    +9

    Откуда столько плюсов у статьи? Вы плюсуете просто за мнение, или за содержание? Вообще не понял где тут статья. Какая-то невычитанная заготовка.


    1. LabEG
      22.01.2023 14:33

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


    1. ht-pro
      23.01.2023 12:56

      Абсолютно согласен. Такое впечатление, будто плюсов наставили "реакт-бегиннеры" - чисто за то, что увидели знакомое слово "реакт".


      1. markelov69
        23.01.2023 14:10
        +1

        Нет, плюсов наставили потому что SCSS + CSS modules гораздо лучше чем styled components.


  1. mobilz
    22.01.2023 05:36

    я юзаю styled в react/react-native приложениях только для того, чтобы ускорить разработку. за десятилетия разработки веб-проектов рука набита писать css `padding-top: 10px;`, а не `paddingTop: 10`


  1. LabEG
    22.01.2023 14:49
    +3

    Статья построенная на стереотипах и собственных ошибках без единого аргумента и доказательства. С полным игнорированием выигрышных моментов в тестах.

    Начиная с баннерной анимации где автор сделал баг и думает что виноват SC и заканчивая тонной ошибочных стереотипов. А еще этот странный пример с евентбасом. Видимо попытка использовать эффект чубаки. Кстати в npm полно пакетов с евентабсом.

    createGlobalStyles - используется для стилизации внешних компонентов. Например для сброса стилей html и body, не писать же на них компонент. Или для стилизации внешней библиотеки, например нотификаций.

    Так же высосаны из пальца тормоза сборки на SC на тайпскрипте и полное игнорирование того что sass тоже собирать надо еще более медленными инструментами. Сборка SC ускоряется в сотни раз если собираться компиляторов на rust, что встроен в NextJS по умолчанию. Сборку на sass не ускорить ни чем.

    И еще десяток ложных не аргументированных утверждений в том же духе.