CSS in JS aneb optimalizace stylů ve velkých aplikacích

Article by Martin Topolánek

originally on medium.com

Jaký existuje rozdíl mezi dynamickým a statickým CSS in JS. Na co si dát pozor při použití styled-components a jak několikanásobně zrychlit načítání vašich webových stránek.

 

Co přináší éra SPA?

Pokud se neuchýlíte ke stylování za pomocí vytváření CSS tříd v čistém CSS nebo nesáhnete po preprocesoru typu LESS nebo SASS, pak použijete nějakou formu CSS in JS. V závislosti na knihovně, kterou použijete se CSS třídy přidávají buďto při sestavování projektu (build-time) nebo za běhu aplikace.

Firefox, výrazně rychlejší než Chrome

Rozdíly v rychlostí renderování mezi prohlížeči Chrome a Firefox byly extrémní. Níže přikládám výsledky profilování načtení stránky ve Firefoxu a Chrome.

Obrázek 1: Profilování načtení stránky v prohlížeči Firefox
Obrázek 2: Profilování načtení stránky v prohlížeči Chrome

Jen podotknu, že celkový rozdíl mezi načtením stránky ve Firefoxu a v Chrome byl cca 30 sekund.

První úspěch

Chrome zobrazuje u úkolů pro přepočet stylů a výpočtu layoutu i iniciátora. Je tedy možné se dopátrat ke zdroji a zjistit, co daný úkol spustilo. Ne vždy však dostanete smysluplnou odpověď. Je možné, že iniciátorem je skript knihovny, na které stavíte nebo jiná část vykreslovacího procesu (například plánované přepočtení stylů, apod.). Může se také stát, že iniciátorem úlohy je část vámi psaného skriptu.

Poslední výše zmiňovaná možnost se stala i nám. Zjistili jsme, že po načtení stránky spouštíme skript pro změnu velikosti písma pro kontejner ve kterém bylo obalených 150 tisíc nodů. Úprava této funkce nám ušetřila 4 sekundy z výsledného času.

Obrázek 3: Demonstrace odhalení iniciátora úlohy z vykreslovacího procesu

Dobrý začátek ale co s těmi zbylými 24 sekundami? Pokud tedy beru v potaz naměřený čas za rendering (viz Obrázek 2 — kruhový diagram), což je část, kterou můžeme, jako frontendisti, do značené míry ovlivnit.

Detektivní práce

Krásná ukázka může být otevření seznamu hodnot selectu na výběr velikosti fontu. Po kliknutí na tlačítko se na pozadí přidá do DOMu list (na pozadí se jedná o ul element). Spolu s touto akcí se přidají CSS třídy určené pro tuto komponentu.

Níže můžete vidět ukázku této komponenty včetně jejího profilování.

Obrázek 4: Demonstrace vyprofilování pomalého úlohy

Z obrázku výše můžeme vyčíst, že rozbalení selectu trvalo cca 8 sekund. Avšak na druhý pokus se select rozbalil okamžitě. Výsledky profilování potvrzují teorii o přidávání CSS tříd do CSSOMu. Iniciátorem úlohy výpočtu layoutu (Layout) byl skript, který přidal na stránku CSS třídy pro zobrazení select nabídky. V momentě, kdy se přidaly CSS třídy, Chrome spustil přepočet stylů a výpočet layotu.

Plynulý přechod od styled-component

GitHub – callstack/linaria: Zero-runtime CSS in JS library

Zero-runtime CSS in JS library. Contribute to callstack/linaria development by creating an account on GitHub.

GitHub – atlassian-labs/compiled: A familiar and performant compile time CSS-in-JS library for…

A familiar and performant compile time CSS-in-JS library for React. Get started now ➚ Turn on extraction and all…

Vybrali jsme si linarii a to na základě této analýzy. Opírali jsme se o fakta, že knihovna existuje na trhu již 5 let, je stále aktivní, implementují se nové úpravy a vývojáři reagují relativně rychle na vzniklé issues. Compiled na druhé straně je dle dostupných údajů na trhu pouhý 1 rok. Chtěli jsme mít jistotu, že knihovna, kterou nasadíme do produkce, je již osvědčená a proto jsme sáhli po Linarii, která je na trhu dostupná delší dobu.

Přechod ale nebyl okamžitý. Přejít z jedné knihovny na druhou nám přišlo příliš komplikované a časově náročné. Proto jsme zkusili nejdřív následující kroky, které bychom museli při přechodu na linarii i tak podstoupit:

  • přepis JS proměnných (například proměnné definující design aplikace, tzv. theme) do CSS proměnných
  • přepis prop atributů do data-atributů

Tyto kroky pomohly k optimalizaci v tom ohledu, že komponenta, která již byla přítomna v DOMu, byla rychlá při změně stavu. Přepis prop atributů na data-atributy způsobil, že se nemusely přidávat nové CSS třídy ale použily se stávající.

Závěrečné zhodnocení

  • při vyvíjení aplikace každá uložená změna způsobí, že se stránka obnoví 3x (souvisí s run-time kompilací stylů)
  • za neznámých okolností se vyhazují zvláštní chyby (např. občas není možné použití forwardRef funkce, když ji linaria rozšiřuje)
  • narozdíl od styled-components nemůžeme psát css funkci, ve které referujeme na jiné styled komponenty

Výhoda používání knihovny však převažuje nad jejími nevýhodami. Jsme otevření jiným možnostem, ale aktuálně je nevyhledáváme.

Níže přikládám výsledky profilování stejné stránky po přechodu na novou knihovnu.

Obrázek 5: Profilování webové stránky za použití Linaria

Z profilování lze vyčíst, že rendering nyní trval 8,5 sekund, čímž je aplikace přibližně 3,5 násobně rychlejší oproti původním 27 sekundám.