121 pomiarów Core Web Vitals. Dwie moje najlepsze hipotezy pomiary odrzuciły
Ten sam adres bez zmiany w kodzie dawał od 60 do 96 punktów. Dwie podręcznikowe optymalizacje wdrożyłem, zmierzyłem i wycofałem. Oto liczby.

👁 115 przeczytań
- Pojedynczy pomiar Lighthouse nic nie znaczy - ten sam adres bez żadnej zmiany w kodzie dawał wyniki od 60 do 96 punktów.
- Sklejenie plików CSS w jeden plik pogorszyło TBT z 76 ms do 372 ms, bo jedno zadanie 69 KB blokuje wątek główny nieprzerywalnie.
- Prawdziwym źródłem rozrzutu był stos reklamowy: po jego zablokowaniu rozrzut FCP spadł z 22% do 0,7%, a wynik ustabilizował się na 77/77.
Optymalizowałem ten serwis przez kilka dni, robiąc po każdej zmianie serię pomiarów Lighthouse zamiast jednego przebiegu. Efekt był upokarzający: dwie z moich najlepszych hipotez okazały się szkodliwe, a prawdziwego winowajcę znalazłem dopiero wtedy, gdy przestałem wierzyć w pojedynczy wynik. 121 pomiarów w dziewięciu seriach. Oto co z nich wyszło.
Trzy wnioski w jednym akapicie
1. Jeden pomiar Lighthouse nic nie znaczy - ten sam adres dawał mi wyniki od 60 do 96 punktów bez żadnej zmiany w kodzie. 2. Sklejenie plików CSS w jeden, czyli rada z każdego poradnika, pogorszyło TBT z 76 do 372 ms. 3. Winowajcą rozrzutu nie był mój kod, tylko stos reklamowy - po zablokowaniu domen reklamowych wynik ustabilizował się na 77/77 z rozrzutem FCP na poziomie 0,7% zamiast 22%.
Aktualna lista kursów AI
Zestawienie odświeżane automatycznie raz na dobę: ceny, liczba wykładów i oceny prosto z katalogu sprzedawcy. Nowości i bestsellery na górze.
Zasada zero: mierz serię, nie pojedynczy przebieg
To brzmi banalnie, ale prawie nikt tego nie robi - i dlatego prawie wszystkie „optymalizacje” w internecie to przypadek.
Uruchomiłem Lighthouse dziesięć razy z rzędu na niezmienionym adresie. Wyniki rozjechały się od 60 do 96 punktów. Gdybym zmienił cokolwiek w kodzie i trafił na przebieg 96 po zmianie i 60 przed, ogłosiłbym sukces. Gdyby wyszło odwrotnie, wycofałbym dobrą poprawkę.
Pro tip: minimum pięć przebiegów na wariant, porównuj mediany i zawsze podawaj pełny zakres. Jeśli zakresy przed i po zachodzą na siebie, nie masz wyniku - masz szum.
Hipoteza 1, odrzucona: sklejanie CSS w jeden plik
Klasyczna rada: mniej żądań HTTP, więc połącz arkusze. Zbudowałem paczkę z kilku plików CSS motywu, sprawdziłem poprawność ścieżek względnych i kolejności importów, wdrożyłem. Pomiar:
| Wskaźnik na stronie wpisu | Przed | Po sklejeniu |
|---|---|---|
| TBT (blokowanie wątku głównego) | 76 ms | 372 ms |
| Czas styleLayout | 700 ms | 1018 ms |
Dlaczego to nie zadziałało: pięć osobnych arkuszy to pięć krótkich zadań parsowania, które przeglądarka może przeplatać z innymi. Jeden arkusz 69 KB to jedno długie zadanie, którego nie da się przerwać - a długie zadania to dokładnie to, co mierzy TBT. Zmniejszyłem liczbę żądań i pogorszyłem odczucie płynności.
Pro tip: przy HTTP/2 liczba żądań przestała być problemem. Sklejanie plików to porada z czasów HTTP/1.1 i dziś potrafi zaszkodzić.
Hipoteza 2, odrzucona: odroczenie zapytań w tle
Strona sklepu wysyłała kilka zapytań AJAX przy starcie. Odroczyłem je przez requestIdleCallback z limitem czasu 3000 ms - podręcznikowe rozwiązanie. Pomiar:
- TBT sklepu: 64 ms → 265 ms
- Start zapytań przesunął się z przedziału 183-1329 ms na 383-2411 ms
Dlaczego to nie zadziałało: limit czasu 3000 ms sprawił, że przeglądarka odpaliła wszystkie odroczone zapytania jedną serią - i to dokładnie w oknie, w którym liczony jest TBT. Zamiast rozłożyć pracę, zebrałem ją w jeden korek.
Pro tip: requestIdleCallback z krótkim limitem czasu to nie jest „zrób to później”. To jest „zrób to później, ale na pewno”. Bez limitu albo z bardzo długim zachowuje się inaczej.
Co zadziałało i dlaczego
Jedna linijka, która skasowała 70 ms wymuszonego przeliczenia układu
Skrypt odczytywał pozycję elementu i od razu ją zmieniał. To powoduje wymuszone przeliczenie układu - przeglądarka musi przerwać wszystko i policzyć geometrię strony. Lighthouse wskazywał 70-75 ms w jednej linii.
Poprawka to jedna linijka:
if (window.requestAnimationFrame) requestAnimationFrame(miejsce); else miejsce();
Odczyt przenosi się do momentu, w którym przeglądarka i tak przelicza układ. Koszt spada do zera, a kod robi dokładnie to samo.
Wyrzucenie z krytycznej ścieżki tego, czego strona nie używa
Arkusz szablonów ważył 75 094 bajty i ładował się na każdej podstronie, mimo że większość stron nie używa żadnego z tych szablonów. Wersja podstawowa to 9 708 bajtów - czyli 87% wagi szło na darmo. Podobnie jQuery, ładowane mimo braku jakiegokolwiek konsumenta.
Pro tip: zanim zaczniesz minifikować, sprawdź, czy plik jest w ogóle potrzebny na tej stronie. Usunięcie bije kompresję każdego dnia.
Prawdziwy winowajca: stos reklamowy
Nie przepłacaj za te subskrypcje
Prowadzę sklep z rocznymi dostępami do narzędzi AI - te same konta, o których piszę wyżej, tylko taniej niż w cenniku producenta.
Po odrzuceniu obu hipotez zadałem inne pytanie: skąd bierze się rozrzut 60-96 punktów. Zablokowałem domeny reklamowe i zgody i powtórzyłem serię:
| Z reklamami | Bez reklam | |
|---|---|---|
| Wynik wydajności | 60-96, dwubiegunowy | stabilne 77/77 |
| Rozrzut FCP | 22% | 0,7% |
| TBT | zmienny | 0 |
| Waga stosu | 445-467 KB | |
Wniosek był brutalny: przez kilka dni optymalizowałem kod, który odpowiadał za ułamek problemu. Rozrzut brał się z zewnętrznych skryptów, na które nie mam wpływu i których nie usunę, bo finansują serwis.
Pro tip, najważniejszy z całego tekstu: zanim zoptymalizujesz cokolwiek, zmierz, ile z wyniku pochodzi od Ciebie, a ile od zewnętrznych skryptów. Zrób serię z zablokowanymi domenami stron trzecich. Jeśli różnica jest duża, Twój realny sufit jest znacznie niżej, niż myślisz - i lepiej to wiedzieć przed trzema dniami pracy niż po.
Dwie pułapki narzędzi, na które trafiłem przy okazji
Pingdom potrafi kłamać o kompresji
Raport twierdził, że serwer nie kompresuje odpowiedzi. Sprawdziłem bezpośrednio: serwer wysyła br i zstd, a gdy poprosić wprost o gzip, oddaje stronę główną w 71 306 bajtach zamiast 289 394 nieskompresowanych. Narzędzie po prostu nie deklarowało obsługi nowszych algorytmów. Nagłówki wygasania też były na miejscu.
Czyszczenie całego cache potrafi zepsuć wygląd
Wtyczka czyściła przy każdej publikacji cały cache razem ze zminifikowanymi plikami CSS. W oknie między skasowaniem a odbudowaniem strona linkowała do arkusza, którego nie było - i przez chwilę wyświetlała się jako goły HTML. Przy kilkunastu wpisach dziennie zdarzało się to regularnie.
Pro tip: po publikacji wpisu czyść punktowo - sam wpis, stronę główną, jego kategorie i kanał RSS. Nowy artykuł nie zmienia zawartości pliku CSS, więc nie ma powodu go kasować.
Lista kontrolna, jeśli chcesz to powtórzyć u siebie
- Zrób dziesięć pomiarów na niezmienionej stronie. Zapisz medianę i zakres. To jest Twój szum.
- Powtórz serię z zablokowanymi domenami zewnętrznymi. Różnica to sufit Twoich możliwości.
- Dopiero teraz zmieniaj po jednej rzeczy naraz i mierz serią, nie pojedynczym przebiegiem.
- Jeśli zakresy przed i po zachodzą na siebie - wycofaj zmianę. Brak dowodu to nie dowód.
- Zacznij od usuwania, nie od minifikacji. Plik, którego nie ma, jest zawsze najszybszy.
- Szukaj wymuszonych przeliczeń układu. Bywają warte 70 ms za jedną linijkę poprawki.
Skąd te liczby
Wszystkie wartości pochodzą z 121 pomiarów Lighthouse w dziewięciu seriach, wykonanych na tym serwisie w dniach 18-19 września 2026 na czterech typach stron: głównej, wpisie, recenzji i sklepie. Obie odrzucone hipotezy zostały wdrożone na produkcji, zmierzone i wycofane. To nie jest zestawienie cudzych porad - to jest sprawozdanie z tego, co u mnie zadziałało i co nie.
Cały tydzień w AI, w jednym mailu
Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.
Zapisz się za darmo →Najczęstsze pytania
Ile razy uruchomić Lighthouse, żeby wynik był wiarygodny?
Minimum pięć przebiegów na jeden wariant, a zakresy przed i po zmianie nie mogą się na siebie nakładać. Autor wykonał 121 pomiarów w dziewięciu seriach i dopiero wtedy wyniki pozwoliły odrzucić błędne hipotezy.
Czy sklejanie plików CSS w jeden plik przyspiesza stronę?
Nie zawsze - w opisanym teście TBT wzrósł z 76 ms do 372 ms po połączeniu kilku arkuszy w jeden plik 69 KB. Przy HTTP/2 liczba żądań nie jest problemem, a jedno długie zadanie parsowania blokuje wątek główny bez możliwości przerwania.
Jak requestIdleCallback z limitem czasu 3000 ms wpłynął na TBT sklepu?
TBT wzrósł z 64 ms do 265 ms, bo limit czasu sprawił, że przeglądarka odpaliła wszystkie odroczone zapytania jedną serią dokładnie w oknie pomiaru TBT. Zamiast rozłożyć pracę w czasie, skrypt zebrał ją w jeden korek.
Co zrobić, gdy zewnętrzne skrypty reklamowe psują wyniki Core Web Vitals?
Przed optymalizacją własnego kodu należy zrobić serię pomiarów z zablokowanymi domenami stron trzecich - różnica to realny sufit możliwości. W opisanym przypadku zablokowanie stosu reklamowego zmniejszyło rozrzut FCP z 22% do 0,7% i ustabilizowało wynik na 77/77.
