Wydajność

INP zamiast FID — case study sklepu WooCommerce z problemem interaktywności

Od marca 2024 INP zastąpił FID jako Core Web Vital. Case study: sklep WooCommerce z INP 890 ms (fail) — co zdiagnozowaliśmy, co naprawiliśmy, INP po 2 tygodniach: 180 ms.

DDawid Penkala
11 min czytania
Dashboard z metrykami Core Web Vitals i INP

Do marca 2024 w Core Web Vitals mieliśmy FID (First Input Delay) — mierzył opóźnienie pierwszej interakcji użytkownika. FID dla większości stron był „zielony”, bo mierzy tylko pierwszą klikniętą rzecz. Google zastąpił go przez INP (Interaction to Next Paint), który mierzy najgorsze opóźnienie reakcji strony na dowolną interakcję w całej sesji. Próg zielony: < 200 ms. Próg czerwony: > 500 ms.

I nagle 40% stron, które wcześniej miały „zielony FID”, ma teraz czerwony INP. Poniżej — case z naszego klienta, sklep WooCommerce ok. 3500 produktów, który przyszedł do nas z INP 890 ms w PageSpeed mobile. Przedstawiam cały proces: diagnoza → fix → wynik.

Punkt wyjścia: co znaleźliśmy

PageSpeed Mobile dla home + PDP + cart:

MetrykaHomePDPCart
LCP3.2 s2.8 s2.4 s
INP890 ms720 ms1240 ms
CLS0.080.110.15

Cart był najgorszy. Każda interakcja (zmiana ilości, usunięcie produktu, zastosowanie kuponu) trwała > 1 sekundy. User widział, że coś się dzieje — czekał — frustrowal się — opuszczał koszyk. W praktyce: porzucanie koszyka 68% (średnia branży ~70%, ale klient wcześniej miał 61%, więc coś się pogorszyło).

Diagnoza — gdzie zjada się czas

INP mierzy trzy fazy opóźnienia przy każdej interakcji:

  1. Input delay — czas od input eventu do start event handlera (event jest w kolejce czekając na wolny main thread).
  2. Processing time — ile sam handler zajmuje.
  3. Presentation delay — czas od końca handlera do kolejnego paintu.

Do audytu używamy Chrome DevTools → Performance panel → Record → kliknij w problematyczny element. Dostajesz wykres czasu (tzw. “flame chart”) pokazujący, gdzie każdy ms poszedł.

Co znaleźliśmy w naszym sklepie:

1. jQuery 1.12 + jQuery Migrate 1.4

Motyw z 2018, dwie stare wersje jQuery ładujące się przy każdym request. jQuery na klik przycisku „+ 1” w koszyku wykonywał event bubbling + selektor global $(document).on(...) → 180 ms input delay.

2. Heartbeat API WooCommerce

Co 15 sekund WooCommerce wysyła POST /wp-admin/admin-ajax.php?action=heartbeat z sesją koszyka. Handler po stronie PHP trwał 320 ms (bo checkował cart expiry, sprawdzał dostępność produktów, odnawiał token). Przy kliku na koszyk w trakcie heartbeat → main thread zablokowany.

3. Google Tag Manager z 18 tagami

Każdy tag to event listener. Handler klika na produkt wysyłał dataLayer.push(...) → 6 tagów reagowało → każdy po 40 ms → razem 240 ms sam GTM.

4. Mini-cart fragment update

WooCommerce default pattern: po dodaniu do koszyka wraca AJAX z fragmentami HTML, które wymieniają mini-cart. Parsowanie 2-3 KB HTML string przez jQuery’s .replaceWith() → 80 ms.

5. Revolution Slider na homepage

Nie bezpośrednio w koszyku, ale konsumował pamięć i CPU cykle tła. Wpływał na input delay na wszystkich stronach.

Fixy — w kolejności impaktu

A. Zdjęcie jQuery (biggest win)

WooCommerce działa bez jQuery od wersji 8.5 (kwiecień 2024). W motywie znaleźliśmy 3 miejsca, które go wymagały — przepisaliśmy na vanilla JS (fetch + event listeners). wp_dequeue_script('jquery') + wp_dequeue_script('jquery-migrate') w functions.php.

Efekt: -45 KB gzipped JS z bundle, -250 ms TBT.

B. Przeniesienie GTM przez Partytown

Z plain <script> w <head> na type="text/partytown". GTM + wszystkie tagi lądują na web worker thread, nie blokują main thread przy interakcjach.

Efekt: -200 ms INP przypadek krytyczny na koszyku.

C. Heartbeat API throttling

W functions.php:

add_filter('heartbeat_settings', function($settings) {
    $settings['interval'] = 60; // was 15
    return $settings;
});
add_action('init', function() {
    if (!is_admin()) {
        wp_deregister_script('heartbeat');
    }
});

Heartbeat wyłączony na froncie, w admin panelu co 60 s zamiast co 15 s.

Efekt: -150 ms przypadek krytyczny INP, ~40% redukcja requestów do admin-ajax.

D. Debounce na zmianie ilości w koszyku

Default WooCommerce po każdym +/- wysyła AJAX update cart. Jeśli user klika +++++ (5 razy) → 5 requestów → ostatni wygrywa, ale main thread zapchany.

Dodaliśmy debounce 400 ms:

let qtyTimeout;
document.querySelectorAll('.qty-input').forEach(input => {
  input.addEventListener('input', () => {
    clearTimeout(qtyTimeout);
    qtyTimeout = setTimeout(() => {
      updateCartQty(input.dataset.productId, input.value);
    }, 400);
  });
});

Efekt: -300 ms INP w przypadek krytyczny koszyk przy szybkim klikaniu.

E. Rewrite Revolution Slider → Swiper

Revolution Slider 2018 = 120 KB + jQuery dependency. Zastąpiliśmy Swiper.js (native, 30 KB). Poza tym zgodne z accessibility (nawigacja klawiaturą), bez CVE w historii.

Efekt: -90 KB z home, ale INP na stronach bez slidera bez zmian.

F. React mini-cart zamiast fragmentów

Tu była większa praca — zamiast jQuery fragments, mini-cart jako niezależny komponent React z własnym state (reaguje na custom event wc-cart-updated). Zero fragment parsing, zero replaceWith — tylko state update → React re-render.

Efekt: -80 ms na każdej aktualizacji koszyka.

Wyniki — po 2 tygodniach

MetrykaHome (przed → po)PDP (przed → po)Cart (przed → po)
LCP3.2 s → 1.8 s2.8 s → 1.6 s2.4 s → 1.4 s
INP890 ms → 140 ms720 ms → 180 ms1240 ms → 220 ms
CLS0.08 → 0.050.11 → 0.060.15 → 0.08

Wszystkie zielone. INP na koszyku 220 ms — granica, ale już zielona. W praktyce: porzucanie koszyka spadło z 68% do 54% w ciągu 4 tygodni po wdrożeniu.

Co zrobić samemu — w kolejności wpływu

  1. Mierzyć INP field data — w PageSpeed Insights sekcja „Real users (CrUX)”. Jeśli > 200 ms — masz problem. Jeśli brak danych — w Chrome DevTools → Performance → nagraj typową interakcję.
  2. Zdejmij jQuery gdzie się da — WooCommerce od 8.5 działa bez. Old themes wymagają audytu.
  3. GTM przez Partytown — w Astro dodajesz integrację jedną linijką, w WordPressie — plugin „WP-Rocket” z opcją delay JS lub bezpośrednio attrybut type="text/partytown" + biblioteka Partytown.
  4. Heartbeat throttling albo wyłączenie na froncie — WordPress wysyła heartbeat w tle nawet na frontpage.
  5. Debounce interakcji formularzy / koszyka — custom JS, ale 20 linijek kodu.
  6. Revolution Slider / Slider RevolutionSwiper — single biggest JavaScript cleanup move dla starych motywów.

Kiedy INP to nie jest problem kodu, tylko hostingu

Jeśli Twój serwer jest za wolny (TTFB > 500 ms), AJAX update koszyka trwa długo nie z powodu JS, tylko czekania na response. Przed ukryciem INP zawsze sprawdź TTFB. Jeśli TTFB > 600 ms → najpierw wąskie gardło backendu (inny artykuł o hostingu i przyspieszaniu WordPressa).


W Devance INP audit to standardowa część naszego darmowego audytu WordPress. Jeśli masz sklep WooCommerce z pogorszoną konwersją bez zmian w produkcie czy cenach — zwykle winne jest INP. Pokażemy gdzie konkretnie i ile będzie kosztował fix.

Tagi:INPCore Web VitalsWooCommercecase studywydajnośćJavaScript
Dawid Penkala
Dawid Penkala

Doświadczony WordPress Developer z ponad 14-letnim stażem w tworzeniu zaawansowanych stron i sklepów internetowych. Specjalizuje się w WordPressie, dedykowanych wtyczkach i motywach.

Więcej o autorze