Optimalizace Next.js aplikace: Jak dosáhnout až 1000násobného snížení síťového přenosu

Martin Topolánek
7/10/2024

Chcete dramaticky zrychlit načítání stránek, snížit zátěž serveru a dosáhnout až 1000násobného snížení objemu přenášených dat na vašich stránkách? V tomto článku se podíváme na to, jak využít potenciál cache prohlížeče k dosažení těchto cílů.

Implementací prohlížečové cache jsme dokázali snížit přenosovou velikost z megabajtů na pouhé stovky bajtů, což vedlo k výraznému zrychlení načítání stránek a snížení zatížení serveru.

Náhled odpovědi získané ze serveru

Náhled odpovědi získané z cache prohlížeče

Jak funguje cache prohlížeče?

Když uživatel poprvé navštíví stránku, prohlížeč odešle HTTP požadavek na server. Server odpoví s obsahem stránky a přidá do odpovědi klíčové hlavičky jako Cache-ControlETag a Last-Modified. Prohlížeč uloží tyto soubory spolu s hlavičkami do své cache, v závislosti na nastaveních určených hlavičkou Cache-Control.

Při následném načtení stránky prohlížeč zkontroluje, zda má požadované soubory uložené v cache. Pokud ano, odešle na server podmíněný požadavek s hlavičkami If-None-Match nebo If-Modified-Since.

  • If-None-Match: Tato hlavička obsahuje hodnotu ETag, která byla uložena při předchozím načtení.
  • If-Modified-Since: Tato hlavička obsahuje datum a čas poslední modifikace souboru.

Server následně zkontroluje, zda se obsah souboru změnil. Odpoví buď:

  • 200 OK s novou verzí souboru, pokud se obsah změnil, a prohlížeč si tuto verzi znovu uloží do cache.
  • 304 Not Modified pouze s hlavičkou, pokud se obsah nezměnil. Prohlížeč pak načte soubor ze své cache.

Diagram ukazuje proces fungování cache prohlížeče při prvním načtení stránky a následném obnovení stránky. Popisuje komunikaci mezi uživatelem, prohlížečem a serverem, včetně podmíněných požadavků a odpovědí serveru s HTTP status kódem 304 (Not Modified) nebo 200 (OK) a aktualizací cache.

Jak Next.js generuje odpověď 304?

Když server Next.js přijme požadavek od klienta, začne jej zpracovávat. V případě HTML dokumentu Next.js server provolá všechny potřebné služby, aby mohl sestavit výsledné HTML. Pro stránky vykreslené pomocí ISR (Incremental Static Regeneration) nebo SSG (Static Site Generation) má Next.js implementovanou vlastní cache. Tato cache je kombinací in-memory LRU cache (primární) a file system cache (sekundární). To znamená, že Next.js nesestavuje HTML při každém požadavku, ale pouze když je to nezbytné.

Next.js server z tohoto HTML vypočítá hodnotu ETag. Pokud v hlavičce požadavku najde If-None-Match, zkontroluje, zda se tato hodnota shoduje s aktuálním ETag. Pokud ano, vrátí odpověď 304 pouze s hlavičkami. V opačném případě vrátí odpověď 200 s novým HTML a příslušnými hlavičkami.

Na co si dát pozor?

Z výše uvedeného vyplývá, že je důležité dbát na to, aby generované HTML neobsahovalo dynamické prvky, které by mohly zbytečně měnit obsah a tím narušovat efektivitu cache. Next.js například posílá do <script> tagu objekt pageProps, který se při hydrataci vytahuje pomocí JSON.parse.

V naší aplikaci jsme například používali následující URL pro CSS soubor:

<link rel="stylesheet" type="text/css" href={`/api/document/fontSize?v=${dayjs().unix()}`} />

V URL byl parametr s časovým údajem, který zajišťoval, že se uživateli vždy stáhne aktuální verze CSS souboru.

Pokud máte nasazený frontend v clusteru s více instancemi (replikami), může to představovat problém. V případě, že stránku vykreslujete pomocí SSR strategie, nemusíte se tím zabývat, protože Next.js automaticky nastaví hlavičku Cache-Control na private, no-cache, no-store, max-age=0, must-revalidate, což zabrání cachování stránky v prohlížeči.

Nicméně ve strategiích ISR a SSG musíte brát v úvahu, že každá instance aplikace má vlastní cache. Pokud máte například load balancer, který přesměrovává požadavky v clusteru na frontendy v pevném pořadí (tedy jeden požadavek jde na první frontend, další na druhý atd.), může uživatel při obnovení stránky dostávat pokaždé jiné HTML. To vede k tomu, že prohlížečovou cache nelze efektivně využít, protože každá instance může vygenerovat mírně odlišné HTML, například kvůli různým timestampům v URL.

Tip na závěr

Pokud využíváte strategie ISR nebo SSG, je důležité ověřit, že vaše stránka vrací pokaždé stejné HTML. Otestujte to opakovaným obnovením stránky a zkontrolujte, zda se generované HTML nemění. To vám pomůže maximálně využít potenciál prohlížečové cache a zajistit rychlé a efektivní načítání stránek.

Závěr

Správná implementace cache v prohlížeči může výrazně zvýšit výkon vašich stránek, snížit zátěž na server a zlepšit uživatelský zážitek. Je důležité se však vyhnout dynamickým prvkům v HTML, které by mohly narušit efektivitu cachování.

Pro další informace o správném nastavení cache v prohlížeči se můžete podívat na MDN Web Docs: HTTP Caching.