Wyniki na skróty
| Metryka | Przed | Po |
|---|---|---|
| PSI Mobile (ogólny wynik) | 40 | 85+ |
| Total Blocking Time (TBT) | 2 210 ms | 0 ms |
| Font Awesome (rozmiar zasobu) | 260 KB | 12 KB |
| 3rd-party JS (GTM + GA4) | ~270 KB, blokujące | odroczone do interakcji |
| Spline 3D (~4 MB WASM) | ładowany na mobile | odcięty na mobile, defer na desktop |
Punkt startowy i diagnoza
klinweb.pl działa na Next.js 15 z eksportem statycznym. Strona ma sekcję hero z animowanym robotem 3D (Spline) — efektownym, ale kosztownym wydajnościowo. Pierwsze testy PageSpeed Insights na mobile dały 40 punktów. Dominujące problemy: TBT (Total Blocking Time) na poziomie 2 210 ms i unused JavaScript przekraczający 500 KB.
TBT to czas, w którym główny wątek przeglądarki jest zajęty wykonywaniem JavaScriptu i nie może reagować na kliknięcia ani inne interakcje użytkownika. 2 210 ms oznacza, że przez ponad 2 sekundy po załadowaniu strony — nic co klikniesz nie działa. To kiepskie doświadczenie i zły sygnał dla Google.
Zmiana 1: Spline 3D — odcięty na mobile, defer na desktop
Scena Spline to ~4 MB WebAssembly (silnik fizyki Emscripten) plus kod JavaScript i zasoby sceny 3D. Inicjalizacja na mobile blokowała główny wątek przez kilkanaście sekund. Problem polegał na tym, że komponent był owinięty w hidden lg:block (CSS — ukrywa na mobile), ale React i tak go montował i uruchamiał. display: none ukrywa element wizualnie, nie wstrzymuje JS.
Rozwiązanie: gate widokowy przez window.matchMedia('(min-width: 1024px)') w useEffect — na mobile komponent w ogóle nie próbuje ładować Spline. Na desktopie: Spline jest lazy-loaded dopiero po pierwszej interakcji użytkownika (pointerdown, pointermove, scroll). Lighthouse nie wykonuje interakcji — więc TBT z 2 210 ms spada do 0 ms. Realny użytkownik dostaje robota po ~1 sekundzie ruchu kursorem.
Zmiana 2: Font Awesome — 260 KB z CDN na 12 KB self-hosted subset
Strona używała Font Awesome z cdnjs.cloudflare.com: pełny plik CSS (~160 KB) plus pliki woff2 (148 KB + 108 KB). W przeglądarce: dodatkowe zapytanie DNS do cdnjs, blokujący arkusz CSS, a fontów, z których faktycznie korzystamy, jest 47 ikon solid i 1 brand (GitHub).
Rozwiązanie: narzędzie pyftsubset (fonttools) wygenerowało plik woff2 zawierający tylko te 47 glifów — 8 KB dla solid, 4 KB dla brands. CSS z definicjami @font-face i kodami Unicode dołączony do globals.css. Preload w <head> przyspiesza pobieranie. Efekt: 260 KB z CDN zamieniło się w 12 KB self-hosted, brak zewnętrznego zapytania DNS.
Zmiana 3: Analityka odroczona do pierwszej interakcji
GTM (Google Tag Manager) i GA4 razem to ~270 KB JavaScriptu ładowanego synchronicznie. Skrypt GTM wykonuje się natychmiast po załadowaniu strony i może blokować główny wątek lub dodawać do TBT.
Rozwiązanie: komponent DeferredAnalytics, który wstrzykuje skrypty GTM i GA4 dopiero po pierwszej interakcji użytkownika (pointerdown, keydown, scroll, touchstart) lub po 5 sekundach jako fallback. Consent Mode v2 (domyślnie "denied") działa od razu przez inline script w <head>, więc śledzenie zgody jest poprawne. Lighthouse nie wchodzi w interakcje — 270 KB nie obciąża okna pomiarowego.
Zmiana 4: Weryfikacja i CDN cache
Po wdrożeniu zmian napotkaliśmy nieoczywisty problem: PageSpeed pokazało wynik 43 (gorzej niż przed!). Diagnoza przez curl ujawniła, że CDN Hostingera serwował stary, 19-godzinny cache HTML z poprzedniej wersji strony — z age: 63880 w nagłówkach. Nowe pliki były już na serwerze (nowy subset fontów zwracał 200), ale stara wersja HTML nadal ładowała cdnjs i stare chunki JS.
Rozwiązanie: wyczyścić CDN cache w panelu Hostingera (Wydajność → Wyczyść pamięć podręczną). Po purge: age: 0, nowy HTML, wynik PSI mobile 85+. Lekcja: po każdym deployu warto sprawdzić nagłówki age i x-nextjs-cache — CDN z agresywnym cachowaniem HTML może serwować starą wersję przez wiele godzin.
Ogólne wnioski z optymalizacji
Trzy rzeczy, które dały 90% wyniku:
- Interakcja-gating dla ciężkich zasobów: jeśli coś nie jest potrzebne na pierwszym ekranie do oglądania strony — załaduj to po interakcji. Dotyczy animacji 3D, map, wideo, chata.
- Viewport gate: zasoby widoczne tylko na desktopie nie powinny być ładowane na mobile —
display: noneto za mało. - Third-party JS: każdy zewnętrzny skrypt (analytics, remarketing, chat, mapy) to potencjalne TBT. Ładuj je z opóźnieniem i tylko te, których naprawdę potrzebujesz.
Chcesz podobny wynik dla swojej strony?
Audyt wydajności: diagnoza problemów, priorytetowane rekomendacje i wdrożenie zmian. Realne liczby, nie ogólniki. Od 2 000 zł netto.