Jak se optimalizuje rychlost webů chytrým načítáním CSS

Posted 31. 05. 2015 / By Petr Soukup / Rychlost

Zatím jsem tu popisoval spíš obecné optimalizace, tak tentokrát přeskočím k trochu zajímavějšímu způsobu. Přestože má obrovský vliv na rychlost načítání (a hlavně dojem z načítání), tak ho neřeší prakticky nikdo a je na čase něco s tím udělat. Slyšeli jste už o critical CSS a asynchronním načítání stylů?

Jak se vykresluje stránka

Když poprvé přijdete na web, prohlížeč si nejdřív načte celý HTML dokument a pak se rozhoduje, co bude dělat dále. Parsuje ho a hledá v něm odkazy na další zdroje. Důsledkem je, že návštěvník kouká na bílou stránku a točící se kolečko. Obzvlášť viditelné to pak je na mobilním připojení.

Javascript

Pokud v dokumentu najde nějaký externí javascript (tzn. <script src="...">), tak ho nejprve stáhne. Ze stránky nezobrazí ani drobeček, dokud javascript nestáhne a nevyhodnotí - u velkých projektů to může chvíli trvat. To ale všichni víme, všichni dáváme skriptům atribut "async" a dáváme je do patičky (díky tomu se na javascript nečeká). Tenhle bod proto přeskočíme, protože skripty v hlavičce mají jen amatéři :)

CSS

V případě CSS je situace podobná, jen o něco složitější. Stejně jako u javascriptu se čeká na externí styly. Také se ale čeká na inline styly, pokud je v nich direktiva @import a stejně tak externí skript čeká na všechny importované - proto nikdy nevkládejte fonty přes @import!

Zatímco javascript můžeme načíst až po stránce a nebude prakticky poznat rozdíl u CSS už to tak snadno nejde. Můžeme ho sice načíst asynchronně, ale návštěvník pak uvidí rozsypanou stránku, která pak najednou problikne na funkční, když se konečně načtou styly.

Critical CSS

Při normálním načítání se servíruje CSS soubor, který obsahuje třeba styly pro košík, přestože je návštěvník na hlavní straně. Můžete sice rozdělit CSS na samostatné soubory podle podstránek, ale s tím je spojena zase velká režie a problém to stejně nevyřeší. Při prvním vstupu na web totiž návštěvník nepotřebuje ani celé styly pro konkrétní stránku - nepotřebuje například CSS pro patičku webu, když se stejně dívá na horní část stránky. Tento problém řeší takzvané critical CSS.

Návštěvník při prvním příchodu potřebuje jenom třeba 1% z celkového CSS. Stačí mu rozložení hlavičky, rozdělení na sloupce a prvních třeba 800px obsahu. U průměrného eshopu se takové CSS vejde do zhruba 1-3 kB a po GZIP kompresi to bude ještě méně, takže CSS můžeme klidně vložit přímo do stránky.

Stránka pak bude vypadat nějak takto:

<html>
 <head>
  <style>
   .sidebar{float:left};.logo{width:150px}........
  </style>
 </head>
 <body>
   // obsah
  <script>
   // asynchronně načíst normální CSS
  </script>
 </body>
</html>

Rozdíl mezi normálními styly a kritickými je vidět na obrázku níže. Na první pohled je to víceméně stejné. Při bližším pohledu ale zjistíte, že třeba v menu chybí odrážky, nejsou tam custom písma, místy je nejednotné odsazení a podobně. Rozdíly tam tedy jsou, ale jelikož v této podobě to návštěvník uvidí třeba jen zlomek sekundy, tak si jich ani nestihne všimnout. Kritickému CSS také úplně chybí styly patičky, ale než tam návštěvník stihne doscrollovat, tak už budou načtené normální styly a ani si toho nevšimne.

Obrázek je trochu zavádějící, protože při prvním načtení ještě nebudou v cache žádné obrázky a budou se teprve postupně načítat.

Vzhled klasického vs kritického CSS

Porovnání načítání

Pro srovnání přikládám graf načítání stránky s critical CSS a bez něj na pomalém mobilním připojení. Je na tom krásně vidět, že zatímco s critical začalo vykreslování už po 1.5 sekundě, tak bez něj až po třech sekundách.

Nevšímejte si toho, že jedno CSS se načítá déle - to je jen problém měření testovací verze.

blokující CSS

asynchronní CSS

Celková doba načítání je ve výsledku stejná, ale dojem z načítání je úplně jiný. Můžete to porovnat na tomto videu:

asynchronní CSS vs. blokující

Implementace

Funguje to parádně, že? Problém je, že implementovat to správně už tak jednoduché není a je potřeba vyřešit několik problémů.

Typové stránky

Cílem critical CSS je, aby obsahovalo skutečně jen to nejnutnější. Musí proto existovat pro každou typovou stránku samostatné. U eshopů je to docela problém, protože může mít třeba každá kategorie jiný vzhled. U nás proto zatím používáme tuto techniku jen na hlavní stránku, ale určitě to budeme rozšiřovat dále.

Změny šablony

Pokud děláte nějaké změny v šabloně, tak je najednou musíte dělat na více místech - v klasickém CSS a pak v kritickém. V ideálním případě navíc těch kritických máte více - pro každou typovou stránku. Hloupé posunutí loga pak znamená změnu třeba v deseti CSS souborech. Doporučuji proto počítat s tím už od začátku a zakomponovat si možnost exportovat jen kritické CSS do SASS šablony.

U našich eshopů jsme si to museli automatizovat. Často si totiž šablonu upravují sami klienti, takže tyto optimalizace musí proběhnout automatem (někdy tu budu muset napsat o našem krutopřísném deploy procesu). Vykreslí se šablona ve PhantomJS, javascriptem se najdou prvky nad přehybem a zjistí se k nim potřebné CSS vlastnosti. Existují na to i různé nástroje, ale nefungují na 100%.

Cache

Critical CSS sice výrazně zrychlí první načtení, ale každé další naopak zpomaluje. Zcela zbytečně se totiž přenáší CSS vložené inline, přestože máme kompletní CSS v cache. U nás to řešíme tak, že při prvním načtení uložíme hash CSS souboru do cookie. Pokud při dalším načtení cookie odpovídá, tak se nic z výše popsaného nepoužije. CSS se načte klasicky blokujícím způsobem, ale protože je v cache, tak nic neblokuje.

Budou sice případy, kdy cookie nebude odpovídat, ale nejhorší co se může stát je, že se použije blokující CSS, přestože není v cache a to není zase taková tragédie.

Testování

Kritické CSS se poměrně obtížně zkouší. Při úpravě šablon eshopu máme například tlačítko, které vynutí použití kritického CSS a naopak zablokuje normální CSS. Problém je, že pohled na takovou stránku je poměrně zavádějící. Při zkoušení v prohlížeči totiž budete už mít načtené obrázky, ale normální návštěvník je neuvidí. V kritickém CSS tak budete chtít spíš řešit jednoduchý placeholder - třeba místo loga udělat obdélník v podobné barvě a podobně.

Praktický nástroj pro zkoušení je WebPageTest.org - skoro všechny obrázky v tomto článku jsou z něj. Druhým nástrojem pak je Google PageSpeed - ten je hodně praktický, protože vám řekne, kolik procent stránky šlo vykreslit bez externích zdrojů a rovnou přidává další optimalizační tipy.

Pořadí načítání

Pokud budete asynchronně načítat více CSS souborů, tak se můžou načítat v různém pořadí, což v důsledku může rozhodit vzhled. Nejpraktičtější proto je sloučit je do jednoho a minifikovat (tím se rovnou i odstraní duplicitní pravidla).

Důsledky

Přestože se critical CSS nijak neprojevilo na celkové době načítání stránky, tak zákaznicí chválí, jak je eshop rychlý (můžete si to vyzkoušet tady). Reklamní kampaně mají vyšší konverze, Google PageSpeed nadšeně zeleně svítí a zavládl světový mír. Zkrátka paráda!

A co vy?

Bavilo by vás řešit podobné problémy? Pojďte k nám kódovat eshopy!

Další díly:

  1. Jak se loví milisekundy (nejen v #nettefw)
  2. Optimalizujeme pro rychlost: Obrázky
  3. Optimalizujeme pro rychlost: HTTPS
  4. Efektivní minifikace Javascriptu
  5. Optimalizujeme: critical+asynchronní CSS


O blogu
Blog o provozování eshopů a technologickém zázemí.
Aktuálně řeším hlavně cloud, bezpečnost a optimalizaci rychlosti.

Rozjíždím službu pro propojení eshopů s dodavateli.