Każda optymalizacja stojąca za idealnym wynikiem Lighthouse
Regularnie uruchamiam Lighthouse na mojej stronie. Wynik utrzymuje się na poziomie 100 przy każdym lokalnym uruchomieniu oraz 100 w Vercel's Real Experience Score.
Te wyniki nie biorą się z generycznej listy kontrolnej. Osiągam je poprzez przenoszenie pracy z przeglądarki na etap budowania (build stage).
Oto dokładnie, jak to robię.
Przenoszenie pracy na czas budowania
Większość poradników zaleca stosowanie lazy-loadingu dla wszystkiego. Ja wolę prerendering. Używam TanStack Start z włączonym prerenderingiem.
To zmienia całą stronę w statyczny HTML podczas budowania. Przeglądarka nie musi wykonywać ciężkiego JavaScriptu tylko po to, aby wyświetlić pierwszą stronę. HTML jest już gotowy w momencie, gdy użytkownik naciśnie enter.
Wstępne obliczanie złożonej logiki
Moja strona główna zawiera mapę świata z 5000 kropkami. Zazwyczaj biblioteka musiałaby sparsować GeoJSON i wykonywać obliczenia na głównym wątku (main thread). To blokuje stronę na 1000 ms.
Rozwiązałem to, przenosząc obliczenia do skryptu budującego.
- Generuję pojedynczy ciąg znaków SVG path dla wszystkich 5000 kropek.
- Renderowanie jednej ścieżki jest dla przeglądarki znacznie szybsze niż 5000 oddzielnych kół.
- Wstępnie obliczam tablice wyszukiwania współrzędnych, dzięki czemu przeglądarka nie wykonuje żadnych obliczeń w czasie rzeczywistym (runtime).
Opóźnienie wynoszące 1000 ms zamienia się w pojedyncze, natychmiastowe renderowanie (paint).
Optymalizacja ładowania czcionek
Używam rel="preload" dla moich głównych czcionek.
Częstym błędem jest zapomnienie o atrybucie crossOrigin. Jeśli go pominiesz, przeglądarka pobierze czcionkę dwukrotnie. To psuje wskaźnik Largest Contentful Paint (LCP). Preloaduję tylko trzy czcionki używane powyżej linii zgięcia (above the fold).
Używanie odpowiednich narzędzi do animacji
Do prostych animacji pulsowania markerów na mapie używam SMIL. Jest to mniej kosztowne niż używanie stanu Reacta do sterowania pętlą animacji. Pozwala to przeglądarce wykonywać pracę na wątku kompozytora (compositor thread).
W przypadku złożonych ścieżek używam motion. Trzymam to prosto. Animuję raz podczas montowania (on mount) i unikam nasłuchiwania pozycji przewijania (scroll positions).
Trzymanie się wektorów i formatu WebP
Jeśli to logo lub kształt, użyj SVG. Jeśli to zdjęcie, użyj WebP. Dzięki temu rozmiar plików pozostaje niski i zapobiega to przesunięciom układu (layout shifts).
Unikanie nadmiernej inżynierii
Nie używam CDN dla obrazów. Nie stosuję złożonego code-splittingu. Moja strona jest mała, więc podział na poziomie tras (route-level splitting) jest wystarczający.
Idealny wynik może być jedynie metryką próżności (vanity metric). Prawdziwym celem jest mierzenie wydajności i przenoszenie jak największej ilości pracy z urządzenia użytkownika.
Moje portfolio: brodin.dev
Kod źródłowy: github.com/NathanBrodin/Portfolio
Prerendering TanStack Start: tanstack.com/start
Paper Shaders: shaders.paper.design
Pełny wpis: https://dev.to/nathan-brodin/every-optimization-behind-a-perfect-lighthouse-score-283n
