Jedna linijka kodu skasowała 70 ms blokady wątku głównego
Lighthouse pokazał 70-75 ms wymuszonego przeliczenia układu w jednym miejscu skryptu. Poprawka zmieściła się w jednej linijce i nie zmieniła tego, co funkcja robi.

👁 117 przeczytań
- Jedna linijka kodu - owinięcie wywołania funkcji w requestAnimationFrame - zbiła koszt wymuszonego przeliczenia układu z 70-75 ms do zera.
- Wymuszony reflow pojawia się, gdy skrypt odczytuje geometrię elementu tuż po zmianie stylu, zmuszając przeglądarkę do natychmiastowego przeliczenia układu strony.
- Wzorzec requestAnimationFrame nie sprawdza się, gdy funkcja ustawia coś widocznym od razu - opóźnienie o jedną klatkę może być gorsze niż 70-75 ms blokady.
Lighthouse wskazywał 70-75 ms wymuszonego przeliczenia układu w jednej linii skryptu. Dopisanie warunku if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); zbiło ten koszt do zera. Funkcja robi dokładnie to samo - zmienił się wyłącznie moment, w którym czyta geometrię elementu.
Wniosek w jednym akapicie
Jeśli skrypt odczytuje wymiar albo pozycję elementu, a chwilę później ten wymiar zmienia, przeglądarka musi natychmiast przeliczyć układ strony, żeby dać poprawną odpowiedź na odczyt. To jest wymuszony reflow i w moim pomiarze zajmował 70-75 ms wątku głównego w jednym miejscu. Przeniesienie całej funkcji do requestAnimationFrame ustawia ją w momencie, w którym przeglądarka i tak przygotowuje kolejną klatkę - odczyt trafia na już policzony układ i nie wywołuje dodatkowej pracy. Koszt spadł do zera. Moja opinia: to najtańsza możliwa poprawka w kategorii przyspieszenie strony, bo nie wymaga przepisywania logiki ani rezygnacji z żadnej funkcjonalności.
Co dokładnie pokazał raport
Zakładka wydajności w Lighthouse ma sekcję, której wiele osób nie rozwija - listę operacji wymuszających przeliczenie układu. Nie jest to ostrzeżenie o „ciężkim skrypcie” ani o zbyt dużym bundlu. To wskazanie palcem konkretnej linii z konkretną liczbą milisekund.
W moim przypadku była to jedna pozycja: 70-75 ms wymuszonego przeliczenia układu przypisane do jednej linii skryptu. Dla porządku - nie chodziło o pętlę po stu elementach ani o animację przeliczaną co klatkę. Jedno wywołanie, jeden odczyt, jeden zapis.
Wahanie 70-75 ms między przebiegami to normalna rzecz. Reflow kosztuje tyle, ile kosztuje przeliczenie aktualnego drzewa układu, a to zależy od tego, ile elementów jest w danym momencie na stronie i jak skomplikowane mają style. Dlatego w tego typu pomiarach podaję zakres, nie jedną wartość.
Dlaczego odczyt przed zapisem jest drogi
Przeglądarka jest leniwa i to dobrze. Kiedy skrypt zmienia style, zmiana ląduje w kolejce i zostaje rozliczona później, razem z innymi zmianami, przy okazji rysowania kolejnej klatki. Jedna operacja przeliczenia układu obsługuje wtedy wszystkie modyfikacje naraz.
Ta strategia przestaje działać w momencie, w którym skrypt o coś pyta. Zapytanie o szerokość, wysokość, pozycję czy przewinięcie musi dostać odpowiedź zgodną ze stanem faktycznym. Przeglądarka nie ma wyboru - porzuca lenistwo, przelicza układ w tej sekundzie i dopiero zwraca liczbę. Wątek główny stoi, dopóki nie skończy.
Stąd nazwa: forced reflow javascript. „Forced”, bo to skrypt wymusza pracę, której przeglądarka sama by w tym momencie nie wykonała. Jeśli taki odczyt wypadnie w środku obsługi zdarzenia albo w trakcie inicjalizacji strony, użytkownik dostaje zamrożony interfejs na czas przeliczenia.
Poprawka, która zmieściła się w jednej linii
Cała zmiana to:
if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn();Zamiast wywoływać fn natychmiast, oddaję ją przeglądarce z informacją: uruchom, kiedy będziesz przygotowywać następną klatkę. W tym momencie układ jest już policzony, bo przeglądarka właśnie do tego się zabiera. Odczyt geometrii dostaje gotową odpowiedź i nie wywołuje dodatkowego przeliczenia.
Druga część warunku to zabezpieczenie na środowiska bez requestAnimationFrame - wtedy funkcja odpala się tak, jak wcześniej. Nie ma ścieżki, w której cokolwiek się nie wykona.
Podkreślam rzecz, która bywa źródłem nieufności przy takich poprawkach: nie usunąłem odczytu, nie zamieniłem go na wartość z pamięci, nie zrezygnowałem z żadnego efektu. Funkcja robi dokładnie to samo, na tych samych danych, z tym samym rezultatem wizualnym. Przesunięta jest tylko o kilka milisekund w harmonogramie przeglądarki.
Przed i po
| Parametr | Przed poprawką | Po poprawce |
|---|---|---|
| Wymuszone przeliczenie układu w tej linii (Lighthouse) | 70-75 ms | 0 ms |
| Liczba pozycji w raporcie wymuszonego reflow | 1 | 0 |
| Moment wywołania funkcji | natychmiast | najbliższa klatka |
| Efekt działania funkcji | bez zmian | bez zmian |
| Liczba zmienionych linii | - | 1 |
Jedna uwaga interpretacyjna. Zero w kolumnie „po” oznacza, że pozycja zniknęła z raportu wymuszonego przeliczenia układu - a nie że przeglądarka przestała liczyć układ. Liczy go dalej, tylko robi to we własnym rytmie, razem z resztą pracy nad klatką, i nie zatrzymuje na to skryptu.
Kiedy ten wzorzec się nie opłaca
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.
To już opinia oparta na tym jednym pomiarze, nie twarda reguła.
Przesunięcie kodu do requestAnimationFrame oznacza, że wykona się on później - o jedną klatkę. Jeśli funkcja ustawia coś, co użytkownik zobaczy natychmiast po wejściu na stronę, i różnica jest widoczna jako mignięcie, opóźnienie może być gorsze niż 70-75 ms zablokowanego wątku. Wtedy lepszą drogą jest rozdzielenie odczytów od zapisów: najpierw wszystkie pytania o geometrię, potem wszystkie zmiany. Reflow zdarzy się raz, nie po każdej parze operacji.
Drugi przypadek: kod, który sam siebie wywołuje w pętli animacji. Tam requestAnimationFrame jest już naturalnym miejscem i dopisywanie kolejnej warstwy niczego nie naprawi - problemem jest wtedy sam odczyt wewnątrz klatki.
W moim przypadku funkcja nie odpowiadała za nic, co musi się stać w pierwszej klatce, więc przesunięcie było bezkosztowe.
Jak to sprawdzić u siebie
- Uruchom Lighthouse na podstronie, która wydaje Ci się ociężała przy pierwszej interakcji - nie na stronie głównej z automatu.
- Rozwiń sekcję dotyczącą operacji wymuszających przeliczenie układu. Szukasz listy z nazwami plików, numerami linii i milisekundami.
- Wypisz pozycje, które przekraczają kilkanaście milisekund. Reszta to szum i nie warto na nią tracić czasu.
- Wejdź we wskazaną linię i sprawdź, czy obok odczytu geometrii znajduje się zapis do stylu, klasy albo atrybutu. Jeśli tak - to jest Twój wymuszony reflow.
- Sprawdź, czy funkcja musi się wykonać natychmiast. Jeśli nie, owiń jej wywołanie warunkiem:
if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); - Uruchom Lighthouse ponownie, w tych samych warunkach - ten sam profil przeglądarki, to samo dławienie procesora, żadnych innych zmian w kodzie między pomiaramidzy przebiegami.
- Porównaj listę operacji wymuszających reflow. Pozycja z Twojej linii powinna zniknąć. Jeśli tylko zmalała, odczyt prawdopodobnie trafia w jeszcze jedno miejsce - szukaj dalej w tej samej funkcji.
- Przejdź przez stronę ręcznie i potwierdź, że nic nie mignęło ani nie przeskoczyło. Ten krok jest obowiązkowy i nie da się go zastąpić raportem.
Skąd te liczby
Pomiar własny, wykonany 26.09.2026. Narzędzie: Lighthouse, sekcja raportu dotycząca operacji wymuszających przeliczenie układu. Wartość przed poprawką: 70-75 ms przypisane do jednej linii skryptu. Wartość po poprawce: pozycja nieobecna w raporcie, koszt zero. Poprawka: if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); - jedna zmieniona linia, bez modyfikacji logiki funkcji. Wszystkie liczby w tekście pochodzą z tego jednego pomiaru; fragmenty oznaczone jako opinia są moją oceną, a nie wynikiem testu.
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
Co to jest forced reflow i dlaczego blokuje wątek główny?
Forced reflow to sytuacja, w której skrypt żąda od przeglądarki geometrii elementu tuż po zmianie stylu, wymuszając natychmiastowe przeliczenie układu strony. Przeglądarka normalnie odkłada przeliczanie układu na później, ale zapytanie o szerokość czy pozycję elementu wymaga odpowiedzi zgodnej ze stanem faktycznym - wątek główny stoi, dopóki przeliczenie się nie skończy.
Jak znaleźć wymuszone przeliczenie układu w raporcie Lighthouse?
W Lighthouse należy rozwinąć sekcję operacji wymuszających przeliczenie układu, która zawiera listę plików, numerów linii i milisekund. Warto szukać pozycji przekraczających kilkanaście milisekund - reszta to szum, który nie jest wart uwagi.
Jak naprawić wymuszony reflow jedną linią kodu?
Wystarczy owinąć wywołanie funkcji warunkiem: if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); Przesuwa to wykonanie funkcji do momentu, gdy przeglądarka i tak przygotowuje kolejną klatkę, więc układ jest już policzony i odczyt geometrii nie wywołuje dodatkowej pracy.
Czy przeniesienie kodu do requestAnimationFrame zmienia działanie funkcji?
Nie - funkcja robi dokładnie to samo, na tych samych danych i z tym samym rezultatem wizualnym. Zmienia się wyłącznie moment wywołania: zamiast natychmiast, funkcja odpala się przy najbliższej klatce przeglądarki.
