Zrychlujeme web:
CSS, cookies a JavaScripty

Lukáš Ptošek   24. 4. 2020


Naučte se, jak nejlépe připravit a doručit CSS soubory na web. Vyzrajte na zbytečné JavaScripty, které vaše stránky akorát zpomalují.

Vítejte u posledního dílu seriálu o zrychlování webu na frontendu. Už jsme si probrali:

Teď nás čeká asynchronní a kritické (critical) CSS, cookies a lehce se podíváme i na JavaScripty. V igloonetu jsme nedávno optimalizovali e-shop Doktor Kladivo, na který se budu občas odvolávat s konkrétními příklady a výsledky. Pusťte se do čtení.

CSS Styly

Styly (CSS soubory) patří mezi první soubory, které na webu stahujeme. Není divu, bez nich bysme viděli jen shluky textů na bílém pozadí. Proto chceme, aby se  stahovaly co nejrychleji a měly co nejmenší velikost.

Datovou velikost CSS jednoduše zmenšíte pomocí procesu tzv. minifikace, během které se odstraní všechny nepotřebné mezery, zalomení a formátování. Z finálního CSS vznikne jeden dlouhý řádek, který je sice pro člověka nečitelný, ale prohlížeči to nevadí.

Minifikace v praxi

Na e-shopu Doktor Kladivo máme jednotlivé komponenty napsané zvlášť v SCSS (preprocesor pro CSS) souborech, které spojujeme GULP skriptem. Ten provede nezbytnou magii pro dosažení nejlepší kompatibility mezi prohlížeči a vše spojí do jednoho finálního minifikovaného CSS.

CSS má jeden problém – prohlížeč čeká, až se kompletně stáhne, a teprve pak ho začne zpracovávat. Do té doby vidíme jen bílou obrazovku (to se týká hlavně pomalejších připojení) nebo postupné načítání webu. Vyřešíme to pomocí critical CSS a asynchronního nastavením CSS stylů.

Critical CSS

Při prvním načtení stránky uživatel potřebuje asi 1 % vašich stylů – stačí mu zobrazení hlavičky, menu a dalších prvků, které má hned vidět. Tuhle důležitou (kritickou) část proto vyčleňte a natvrdo vložte do hlavičky HTML. Zbytek ať se načítá už z plného CSS. Úvodní stránka se tím zobrazí mnohem rychleji, protože nemusí čekat na stažení všech stylů.

Vlevo vidíte stránku ještě bez stylu a s critical CSS, vpravo je plně načtená. Červená čára označuje konec okna prohlížeče.

Jak critical CSS získat? Vím o dvou možnostech. Pro Gulp je to Critical a pro Grunt a je to Grunt-criticalcss. Je jedno, který způsob použijete. Potřebujete dostat inline CSS, který poté vložíte do hlavičky vašeho HTML.

<style>
/* kritické inline css styly */
</style>

<script>
/* vaše asynchronní styly.css, povíme si dále */
</script>

Critical CSS vám pomůže při první návštěvě webu, ale pak stránky jen zpomaluje, protože bude zobrazovat něco, co už máte stažené. Kolegové na backendu mi proto nachystali funkci $loadCritical, která uživateli vytvoří cookies s hodnotou názvu staženého CSS souboru i s hashem. Pokud na webu neproběhne žádná změna ve stylech, a uživatel ho navštíví podruhé, název souboru na serveru i v cookies bude mít stejný. Critical CSS se díky tomu nezobrazí a ani se nestáhne nové CSS (viz dále). Pokud jsme nějakou změnu v CSS udělali, critical CSS se znovu zobrazí, a také se stáhnou nové změněné CSS soubory.

Nevýhoda critical CSS je v tom, že pokud ho nemáte plně automatizované, anebo děláte často změny v kritické oblasti, je hodně pracné a složité na údržbu. Pak záleží, jestli se to vyplatí.

<style type="text/css" n:syntax=off n:if=$loadCritical>
html{-webkit-font-smoothing:antialiased;background-color:#fff;color:#343943;height:100%;line-height:1.15} ... pokračování stylu
</style>

$loadCritical nám zajistí, že se critical CSS zobrazí jen při první návštěvě webu.

Asynchronní načítání CSS

Proč řešit asynchronní načítání? Než se CSS stáhne, uživatel vidí pouze bílou obrazovku. To samozřejmě nechceme.

Na asynchronní CSS používáme JS knihovnu od filamentgroup s názvem loadCSS. Na webu máme dva CSS soubory – jeden pouze na ikonky (moc často je neměníme, proto soubor zvlášť), druhý na vše ostatní. Kvůli paralelnímu stahování pomocí HTTP/2 by bylo lepší vše rozdělit na ještě více souborů podle webových komponent. Doporučuju postupovat podle návodu, náš kód má ale mírné zlepšováky o cookies (viz níže).

Stejně jako u critical CSS platí, že pokud přijdete na web a CSS soubory už máte stažené, nemusíte vůbec spouštět poslední JavaScript (podmínka $loadCritical ho vyřadí) a vykonávat celý proces uvnitř. Pokud CSS ještě nemáte, poslední JavaScript se spustí, a pomůže prohlížečům, které neumí funkci preload (preload předpřipraví data, aniž by blokoval vykreslení zbytku webu, podrobněji jsem o něm psal v minulém dílu).

Pojďme si rozebrat, co dělá upravený link oproti klasickému zápisu:

  • rel – podle toho, zda už máte stažené CSS, se dá preload nebo stylesheet;
  • href – odkaz na CSS soubor i s hashem;
  • as – nastavte, co se bude stahovat (jinak se všude přiřadí minimální priorita, a to nechcete);
  • onload – po stažení nahradí preload v rel za stylesheet, a tím zobrazí CSS. Máme zde ještě ochrannou pomůcku this.onload=null, protože některé prohlížeče by po dokončení načítání mohly začít znovu načítat.
<style type="text/css" n:syntax=off n:if=$loadCritical>
Critical CSS
</style>

/* asynchronní css styly */
<link rel="{$loadCritical ? preload : stylesheet}" href="{versionedFile '/css/styles.css'}" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{versionedFile '/css/styles.css'}"></noscript>

<link rel="{$loadCritical ? preload : stylesheet}" href="{versionedFile '/css/icons.css'}" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{versionedFile '/css/icons.css'}"></noscript>

/* javascript na asynchronní styly */
<script n:syntax="double" n:if=$loadCritical>
!function(t) pokracovani scriptu dále
</script>

Se správným kódem:

  • eliminujete bílou obrazovku při načítání CSS souborů;
  • eliminujete blokování ostatních prvků dále na stránce tím, že se CSS soubory stahují na pozadí;
  • pomoci cookies eliminujete opětovné načítání critical CSS a JavaScriptu na asynchronní CSS, tím trochu urychlíte načítání.

Může se stát, že vám bude Google v PageSpeed Insights psát, že CSS soubory blokují načítáni zbytku webu. Není to pravda, uživatel má soubory už v cache. Mám dojem, že tuhle chybu už opravili, ale raději to berte v potaz.

JavaScripty

JavaScripty jsou největší brzdou webu, zvlášť ty od třetích stran. Čím méně se jich při startu spustí, tím lépe.

Aktuálně máme na webu Doktor Kladivo v plánu přepsat JavaScript soubory do modernějšího a rychlejšího zápisu, nicméně jsme se z aktuálně použitých snažili vyždímat maximum. Jak jsme zhruba postupovali? Prošli jsme si každý JS soubor a posoudili, jestli ho ještě používáme. Bylo tam třeba pár zastaralých knihoven na sjednocení fungování některých vlastností prohlížečů nebo na řešení nových vlastností CSS, které tehdy prohlížeče neuměly – vyhodili jsme je. Dále jsme všechny knihovny aktualizovali na nové verze, některé jsou lépe napsané, menší, a jsou v nich opravené chyby. Z 640 kB jsme se například dostali na 504 kB, což je úspora 22 %.

Také jsme nastavili, aby se některé JS generovaly jen na určitých stránkách. JS pro galerii produktů je například zbytečné vkládat do stránky globálně, nově ho přidáváme jen do detailu produktu přes PHP presenter. Z PHP kompilátoru jsme ještě vyřadili všechny JS, které se používají na celém webu. Do stránky je nově vkládáme jednotlivě, abychom využili paralelní stahování a zpracování v HTTP/2.

Z <head> tagu jsme také odstranili všechny JS nepotřebné pro start webu. Zůstalo tam jen to nejnutnější: asynchronní načítání CSS a Google Tag Manager. Ostatní šlo na konec <body> tagu.

Co máme na konci <body> tagu? Postupně v tomhle pořadí:

  1. Lazyload JavaScript na postupné načítání obrázků (podrobněji jsem o něm psal v dílu o obrázcích)
  2. Jednotlivé JavaScripty, které se používají globálně
  3. Vygenerované a spojené JavaScripty pro konkrétní stránky (z jednotlivých presenterů v PHP backendu). Například JS pro galerii produktů.
  4. Další lazyload JavaScript pro specifické bloky na webu (slidery výrobců, prvky doporučených produktů), které jsou níže na webu, a nepotřebují okamžité načtení.
  5. Facebook skript pro marketing.
  6. Webové notifikace, které spouštíme s parametrem defer. Ten se spustí až po všech skriptech, ale zachovává pořadí spouštění.
  7. Cookie lišta.
  8. Defer skript na chatovací okno, které není potřeba v prvních sekundách.

Místo jednoho velkého souboru, který by blokoval stránku, raději postupně zpracováváme více menších. Do budoucna plánujeme převést JavaScripty na nějaký JavaScript kompilátor se správou balíčku.

Postupem času chceme vyřadit kompletně celé jQuery a přepsat JS soubory do modernějšího zápisu, aby se lépe spravovaly. To aktuálně nebylo možné, protože je například soubor X závislý na jiném souboru, a ve výsledku by to znamenalo přepsání velké části JS. Není to záležitost na 5 minut, chce to hodně testovat, zkoušet atp. Jedna chyba vám může shodit celý JS, a tím i návazné věci (filtry, vkládání do košíku atd).

A to je prozatím konec. Celé odvětví kolem rychlosti webu se teprve formuje a na nějaké standardy se teprve čeká. Uvidíme, jak to bude za pár let - možná se těmhle radám budeme smát a vše bude jinak :). Děkuji za přečtení celého čtyřdílného seriálu, pokud máte jakékoliv dotazy nebo zajímavé poznatky, podělte se o ně v komentářích. Třeba věci řešíte úplně jiným způsobem, moc rád se o něm dozvím. Koho by zajímaly přesné časy zrychlení e-shopu Doktor Kladivo, naleznete je v prvním díle.