Zrychlujeme web - Jak na obrázky?

Lukáš Ptošek   6. 12. 2019


Hurá, slevy! Nadšeně otvíráte seznam produktů, ale ten se nenačítá a nenačítá... V čem udělal programátor chybu? Pojďme si ukázat, na co dát u obrázků pozor a jak vám může jednoduchá změna načítání zrychlit web.

Vítejte u druhého dílu igloonetího seriálu o zrychlení webu. Už jsem vám vysvětlil, proč je rychlost webu důležitá a jak ji měřit, v tomhle článku se blíže podíváme na obrázky. Prozradím vám, jaké formáty byste měli kde používat a jak nasadit odložené načítání neboli lazyloading – je přece zbytečné zpomalovat stránky načítáním prvku, který je někde úplně dole. Lazyloading je pro zrychlení webu zcela klíčový, a měla by to být jedna z prvních věcí, kterou nasadit.

Pokud vám moje rady přijdou užitečné, nenechte si ujít další:

Než začneme, budete potřebovat protokol HTTP/2 - na verzi jedna vám nebude fungovat spousta optimalizací, o kterých budu psát. Navíc umožňuje paralelní stahování. S jeho nasazením vám bohužel neporadím, nejlepší bude obrátit se na svého administrátora.

Zmenšení datové velikosti

Každý prvek na webových stránkách (obrázek, JavaScript, CSS styl…) má nějakou datovou velikost. Čím víc dat bude návštěvník stahovat, tím pomaleji mu web samozřejmě pojede. Zamyslete se proto, jaké obrázky ke svým produktům nahráváte a zda hned nespouštíte zbytečná vyskakovací okna. Nutno podotknout, že velikost souboru není jediný ukazatel rychlosti. Stejně velký obrázek a JavaScript se sice stáhnou stejně rychle, ale už se nebudou vykonávat stejnou dobu. Obrázek se po stažení vykreslí, JavaScript se musí ještě přečíst, vykonat, a navíc blokuje další části stránky.

Na webu doktorkladivo.cz můžete narazit na tyto datové formáty:

  • Produktové i statické obrázky;
  • CSS styly, JavaScripty a písma (webové fonty).

V tomhle článku vám podrobněji vysvětlím problémy kolem obrázků, na zbytek se podíváme příště.

Obrázky

Zvlášť u e‑shopů platí, že hodně obrázků znamená hodně dat. Servírujte je proto ve správném formátu a v co nejmenší datové velikosti. Nejrozšířenější jsou:

  • GIF – pouze pro animované obrázky.
  • JPG – ideální formát pro fotky a další nevektorové rozsáhle obrázky. U větších obrázků můžete využít jeho dobrou kompresi a zobrazování v progresivním módu (viz níže).
  • PNG – největší předností PNG je možnost průhlednosti. U větších obrázků má zbytečně velkou datovou velikost, doporučuji proto doplnit pozadí a použít spíše JPG.
  • SVG – vektorový formát, který při zvětšování a zmenšování rozměrů nepodléhá ztrátě kvality. Hodí se pro jednoduchá loga a ikonky.
  • WebP - formát nové generace, který zatím není podporovaný všemi prohlížeči (v době psaní článku ho neumí Safari), ale je v pořádku použitelný. Kombinuje výhody GIFu (animace), JPG (velikost) a PNG (průhlednost). Oproti původním formátům dosahuje o 30-35 % menší velikosti.

WebP se zapíše pomocí picture tagu. Pokud ho prohlížeč umí, použije první řádek. Pokud ne, přeskočí a stáhne JPEG.

 
  
  ...

V ideálním případě by mělo administrační rozhraní obrázek automaticky zpracovat, aby byl pro návštěvníky co nejlepší (například pro produktové fotky by mělo zvolit kombinaci JPG/WebP). Pokud to vaše rozhraní neumí, musíte si formáty hlídat sami.

Ke zmenšování datové velikosti obrázků používám tyto služby pro bezztrátovou kompresi:

  • Tinypng - on‑line aplikace na zmenšení JPG a PNG obrázků;
  • SVGOMG - on‑line aplikace na zmenšení SVG obrázku bez zbytečného balastu v kódu.

Pro komprimaci obrázku nahraných přes administraci můžete použít tyto knihovny, jde spíš o část pro backend vývojáře:

U e‑shopu Doktor Kladivo jsem u statických obrázků dosáhli zmenšení z 1112 kB na 834 kB, tedy o 25 %. Nemusí se to zdát jako moc, obrázky totiž v minulosti už jednou kompresí prošly. Z naší strany však šlo o kompresi bez ztráty kvality, jde tedy o dobrý výsledek. Některé obrázky jsme z formátu PNG převedli na JPG, což je datově menší formát. Využili jsme také funkci progresivního načítání – obrázek se ihned načte v horší kvalitě, a postupně se „zlepšuje.“ Uživatelský prožitek je mnohem lepší než u klasického načítání po řádcích.

Lazyloading obrázku a bloků

Největší zrychlení webu vám u obrázků přinese lazyloading. Základní myšlenka je jednoduchá: „Proč načítat něco, co uživatel ještě nevidí?“ Na webu se totiž běžně načítá úplně vše, zbytečně tak stahujeme data na pozadí a „zpomalujeme“ si stránku. Pojďme si ukázat, jak odložit načítání některých prvků na později, a tím zrychlit celkové načítání, protože obrázky produktů nemají úplně nejmenší velikost a je jich hodně.

Pro tzv. lazyload jsem vybrali JavaScript knihovnu s názvem lazyload od uživatele verlok. Je napsaná v čistém JavaScriptu a využívá IntersectionObserver API (asynchronně hlídá, jestli se blížíme okrajem stránky k určitému bloku), takže máme přímo podporu od prohlížečů za minimum hw nároků.

Plugin ve výchozím stavu vše lazyloaduje, zápis obrázku v kódu tak může vypadat už takhle jednoduše:

 A lazy image

Na projektu používáme Latte od Nette frameworku, náš zápis proto vypadá takhle:

{$product->title}

Class unveil tam máme z důvodu, abychom mohli říct, které obrázky chceme lazyloadovat. Unveil = ano, chci lazyload. Tag noscript zajišťuje, aby se v případě vypnutého (či nefunkčního) JavaScriptu obrázek návštěvníkovi zobrazil, zároveň funguje jako náhrada za původní obrázek pro vyhledávače. Celý skript funguje na jednoduchém principu. Pokud se blížíme k lazyload obrázku, data z data‑src vloží do src, přiřadí k němu adekvátní class, a tím ho zobrazí.

V praxi jsme museli projít celé stránky Doktora Kladiva a všude upravit vypisování obrázků. Byla to fuška, ale výsledek stojí za to. Lazyloadem jsme značně urychlili načtení stránky a nepotřebné prvky jsme odložili na později, takže se web může plně věnovat vykreslení viditelných prvků.

Do CSS musíme ještě přidat zápis, který nebude při vyplém JS v prohlížeči  zobrazovat obrázky připravené na lazyload, ale vypíše jen ty v noscript tagu. Logicky bysme pak nic neviděli.

.no-js {
  .unveil {
    display: none;
  }
}

CSS na lazyload obrázku i s komentáři:

img {
  width: auto;
  max-width: 100%;

  &:not([src]) { // nezobrazovat obrázek, pokud nemá src
    visibility: hidden;
  }

  &.unveil { // nezobrazovat obrázek, pokud se nenačetl
    opacity: 0;
    transition: opacity 1s; //lehká animace, aby se obrázek načetl pozvolně
  }

  &.initial,
  &.loaded,
  &.error {
    opacity: 1; // zobrazit obrázek, pokud se načte a nebo vyhodí chybu
  }
}

Celý lazyloading spustíte jednoduchým skriptem. Před koncem HTML tagu stačí napsat:


 //pripojena lazyload knihovna

Pokud použijete nějaký fltr (např. u výpisu produktů) a překreslí se tím seznam výsledků, nezapomeňte znovu zavolat lazyload, jinak se nové obrázky nezobrazí.

if (typeof lazyLoadInstance === "object") {
    lazyLoadInstance.update(); //zavolej znovu lazyload funkci
}

Na Kladivu jsme se rozhodli lazyloadovat i prvky jako různé slider kolotoče a bloky, které jsou níže na webu. K tomu jsme si napsali jednoduchou funkci s pomoci Intersection Observer API.


Hned první první podmínka (if) rozhodne, jestli prohlížeč umí IntersectionObserver. Pokud ne, blokům ihned přiřadí class lazyload--loaded, a tím je zobrazí. Pokud prohlížeč IntersectionObserver umí, tak všem blokům s class lazyload říká, aby čekali, až se okraj stránky přiblíží na nastavenou vzdálenost (v našem případě 300 px), a teprve poté přiřadí výše zmíněnou class.

Do CSS jsme ještě museli dodat:

.lazyload > div { // z pocatku je blok skryty
  display: none;
  opacity: 0;
  transition: opacity 1s;
}

.lazyload--loaded > div { // pokud priradime classu, tak zobrazime
  display: block;
  opacity: 1;
  transition: opacity 1s;
}

.no-js {
  .lazyload > div { // pokud je v prohlížeči vyplý JS, rovnou zobrazit blok
    display: block;
    opacity: 1;
  }
}

Poskakování obsahu a obrázků

Pro úplnost ještě zmíním problematiku poskakování obsahu. Když prohlížeč stahuje obrázky, většinou neví, jak budou velké. Proto se po stažení celý obsah posune (poskočí), aby se obrázek na dané místo vlezl. Doporučuji na to myslet předem, a jasně vymezit, kde se má zobrazit. Jedná se o takzvaný padding-bottom trik - jestli se o něm chcete dočíst více, odkážu vás na skvělý  článek od pana Michálka, který tuto problematiku detailněji řeší.

Pokud vás k obrázkům na frontendu zajímá něco dalšího, nechte mi komentář. V dalších dílech se podíváme na webové fonty, stahování CSS souborů a JavaScripty.