Битва css-in-js

Битва css-in-js

От автора: мы на сайте HelloFresh постоянно боремся со стилями. Еще год назад стили на нашем сайте были плохими, запутанными, кода было слишком много, как и у множества других компаний. Мы захотели поправить эту ситуацию и нашли новенькую библиотеку css-in-js.js, очень крутая штука.

В итоге мы пришли к Aphrodite, потому что:

крутое название;

ее написали Khan Academy, а значит, они, скорее всего, тоже ее используют.

Это был идеальный вариант для нас, остальные не подходили нам по определенным критериям. Некоторые из инструментов были связаны с другими фреймворками, например, Radium связан с React.

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее

Сейчас, пару месяцев спустя мы решили пересмотреть свой выбор (см. как мы создавали все приложение здесь). Нам нравился наш выбор, но мы хотели посмотреть на что-то новое. К счастью когда мы создали приложение, мы решили добавить некий слой интерфейса между реальными выходными данными в classNames компонента и используемой нами библиотекой. Рефакторинг не вызвал особых затруднений (к счастью).

Среди вариантов были (по крайней мере, из того, что мы нашли):

Aphrodite, все еще мощная штука, да и название крутое.

Glamor, очень нравится API на правилах.

JSS, отличный логотип, хорошее API, можно создавать вложенные стили.

CXS, функциональный CSS, ЧТО!?

Битва

Мы взяли все библиотеки и сравнили их вес, API и производительность.

Производительность

Первый тест был простым:

const styleLib = require('style-lib');

const body = `<div className=${generateClassName({
 backgroundColor: 'blue',
})}></div>`;

return `
 <html>
 <head>
 <style type="text/css">${styleLib.generateCss()}</style>
 </head>
 <body>
 ${body}
 </body>
 </html>
`; 

Конечно, это упрощенная версия, и она не будет работать из-за style-lib. Но этот пример хорошо показывает именно то, как мы тестировали.

Тесты проводились на Macbook Pro с большим объемом памяти и CPU, тесты запускали при помощи Benchmark.js.

Попытка 1

Результаты первого теста:

5 tests completed.

aphrodite x  3,178 ops/sec ±13.32% (43 runs sampled)
jss x 107 ops/sec ±67.27% (8 runs sampled)
jss-without-preset x  71.44 ops/sec ±7.87% (45 runs sampled)
glamor x 11,968 ops/sec ±9.31% (50 runs sampled)
cxs x  7,774 ops/sec ±9.49% (56 runs sampled)

Fastest is: glamor

Как видно, медленным был JSS, очень медленным. Тьяго, один из наших front-end разработчиков завел доработку, о которой можно прочитать здесь. Оказалось, что мы все время писали поверх глобального JSS объекта и не переустанавливали его. Стандартная реализация не такая убогая, как у нас.

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

5 tests completed.

aphrodite x  6,088 ops/sec ±7.65% (62 runs sampled)
jss x 11,291 ops/sec ±8.39% (75 runs sampled)
jss-without-preset x 11,622 ops/sec ±17.20% (52 runs sampled)
glamor x  6,623 ops/sec ±14.67% (67 runs sampled)
cxs x 12,121 ops/sec ±3.80% (75 runs sampled)

Fastest is: cxs,jss

Помимо создания классов, с помощью компонентов React мы также рендерили HTML. Мы поняли, что, в принципе, без этого можно обойтись и продолжили тест уже просто с отрисовкой HTML в виде строки. Как видно, общая производительность поднялась достаточно хорошо.

Мы запустили тест еще раз, в этот раз с cxs/optimized, библиотекой от CXS, которая должна повысить производительность в браузере.

Мы решили измерять длину строки также на выходе из библиотеки, что будет включать отрисованный HTML и CSS.

aphrodite length 470
jss length 447
jss-without-preset length 439
glamor length 422
cxs length 400
cxs-optimized length 445
  6 tests completed.

  aphrodite x  8,943 ops/sec ±14.55% (68 runs sampled)
  jss x 11,697 ops/sec ±27.81% (55 runs sampled)
  jss-without-preset x 46,684 ops/sec ± 7.89% (62 runs sampled)
  glamor x  5,042 ops/sec ±14.27% (47 runs sampled)
  cxs x 19,122 ops/sec ±10.24% (69 runs sampled)
  cxs-optimized x 12,843 ops/sec ±10.52% (69 runs sampled)

Fastest is: jss-without-preset

Расширяем тест

Конечно, тестировать один класс не составляет труда для любой библиотеки. Поэтому мы пошли дальше и добавили тест, который создает много классов с одинаковыми стилями. И еще один, который создает много классов, но уже с разными стилями (просто padding-left с инкрементом).

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

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее

Тест по перегрузке классов

aphrodite length 3044
jss length 3085
jss-without-preset length 3064
glamor length 1935
cxs length 1943
cxs-optimized length 1943
  6 tests completed.

  aphrodite x 1,145 ops/sec ±23.06% (55 runs sampled)
  jss x 1,305 ops/sec ±31.53% (39 runs sampled)
  jss-without-preset x 2,723 ops/sec ±17.48% (38 runs sampled)
  glamor x 2,698 ops/sec ±18.00% (48 runs sampled)
  cxs x 1,697 ops/sec ±15.10% (46 runs sampled)
  cxs-optimized x 2,359 ops/sec ± 7.45% (72 runs sampled)

Fastest is: jss-without-preset,glamor

Тест по перегрузке стилей

aphrodite length 3594
jss length 3509
jss-without-preset length 3430
glamor length 3298
cxs length 3022
cxs-optimized length 3063
  6 tests completed.

  aphrodite x 853 ops/sec ±19.51% (54 runs sampled)
  jss x 2,200 ops/sec ±10.85% (66 runs sampled)
  jss-without-preset x 4,301 ops/sec ±17.48% (55 runs sampled)
  glamor x 665 ops/sec ±17.53% (56 runs sampled)
  cxs x 1,032 ops/sec ±24.12% (43 runs sampled)
  cxs-optimized x 743 ops/sec ±21.16% (45 runs sampled)

Fastest is: jss-without-preset

CXS и Glamor отлично справились со слиянием классов, но в тесте с разными стилями все показали примерно одинаковые результаты. Выделился только CXS.

Все библиотеки не стали сливать классы на верхнем уровне (когда стили одинаковые), посмотреть это можно ниже:

Битва css-in-js

Если рассматривать операции посекундно, JSS без предустановок выигрывает по всем фронтам, а Glamor плохо себя показал с разными стилями. Если бы вы на самом деле использовали JSS с заданной библиотекой, то преимущества у победителя, судя по тесту, минимальны. CXS определенно отличный вариант, но и JSS с предустановками по умолчанию занимает достойное второе место, им удалось достичь хорошей производительности ядра без жестких предустановок.

Победитель: CXS

Размер пакета

Вся сила библиотеки ничего не стоит, если она много весит. Поэтому дальше мы сравнили размеры при запаковке через Webpack. Результаты:

Size cxs 9.766KB
Size cxs-optimized 12.668KB
Size jss-without-preset 24.183KB
Size jss 37.04KB
Size aphrodite 18.919KB
Size glamor 35.436KB

Как видите, JSS – самая быстрая библиотека, но и одна из самых больших. Результаты CXS тоже очень хорошие, разработчики не соврали. Функциональные библиотеки должны иметь небольшой размер. Glamor говорит в файле README, что у них маленький вес, но на самом деле по сравнению с остальными вес не такой уж и маленький.

Победитель: CXS

API

Хорошее время обработки и небольшой вес – это очень важно, но нам также нужно работать с этими библиотеками каждый день. API также очень важный аспект.

Ниже мы покажем, как используются API в упрощенных формах.

Aphrodite

import { css, StyleSheet, StyleSheetServer } from 'aphrodite';

const styles = StyleSheet.create({
 container: {
 backgroundColor: 'blue',
 },
});

const className = css(styles.container);

const { html, css } = StyleSheetServer.renderStatic(() => '<html></html>'); 

В Aphrodite код очень подробный, и они стараются далеко не отходить от обычного CSS. В API используются слова типа StyleSheet и css. Метод renderStatic отрисовывает CSS в виде строки.

CXS

import cxs from 'cxs';

const className = cxs({
 backgroundColor: 'blue',
});

const { css } = cxs; 

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

Glamor

import { style } from 'glamor';
import { renderStatic } from 'glamor/server';

const className = style({
 backgroundColor: 'blue',
});

const { html, css, ids } = renderStatic(() => '<html></html>'); 

API Glamor заимствует отдельные элементы из API CXS и Aphrodite. Также Glamor требует, чтобы HTML возвращался в колбэк-функцию.

JSS

import jss from 'jss';

const { classes } = jss.createStyleSheet({
 container: {
 backgroundColor: 'blue',
 },
}).attach();

const css = jss.sheets.toString(); 

API JSS понять немного сложнее. Он заимствует часть у Aphrodite, а рендеринг CSS больше похож на CXS.

Победитель: CXS

Интеграция с клиентом

Предыдущие тесты брали в расчет только рендеринг кода на сервере. Если вы рендерите CSS на сервере, вам не нужно делать те же самые операции в клиенте. Так какая из библиотек лучше всего с этим справляется? Вычислить это с помощью тестов довольно сложно. При переходе с сервера в клиент нам нужно было бы написать что-то заумное на PhantomJS. Мы подумали, что объяснить и сравнить, что каждая библиотека делает, будет намного лучше.

Aphrodite и Glamor осторожно генерируются заново перед разворачиванием в клиенте. Glamor хорошо объясняет весь процесс здесь. Однако ни CXS ни JSS не думают о том, что уже было отрисовано на сервере. В инструкции к CXS говорится, к примеру, что нужно просто удалить тег style, который попал туда с сервера после того, как клиент сгенерирует весь CSS.

Победитель: Glamor

Заключение

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

Я считаю, что явного победителя нет. У нас есть реальная проблема с повторной генерацией. Как правило, стилей у нас много, и нам приходится повторно их генерировать для одной страницы, что довольно расточительно. Я создал заявку на доработку в репозитории JSS на эту тему, и мне дали пару хороших советов. Надеемся, что эта битва будет иметь хоть какую-то пользу!

И да, не забудьте посмотреть исходники тестов! Не стесняйтесь добавлять свои css-in-js библиотеки!

Редакция: Pepijn Senders

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

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

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее
Самые свежие новости IT и веб-разработки на нашем Telegram-канале

Практика HTML5 и CSS3 с нуля до результата!

Получите бесплатный пошаговый видеокурс по основам адаптивной верстки с полного нуля на HTML5 и CSS3

Получить

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree