Notes · Notatka 001 · Wydajność webu

Gdzie statyczna strona ukrywała ostatnie 18 punktów

Obrazy były skompresowane, JavaScript mieścił się w trzech plikach, a frameworka nie było. Lighthouse nadal pokazywał 82. Brakujące punkty kryły się w trzech miejscach, które narzędzie nazwało, lecz nie wyjaśniło.

Serhat Belen

Założyciel, BLN Global · · 7 min czytania

Krótka odpowiedź

Obrazy i JavaScript były już w porządku. Punkty traciliśmy w trzech miejscach. Po pierwsze, kontrola pomijająca zbędną pracę sama odczytywała układ i zmuszała przeglądarkę do obliczeń. Po drugie, arkusz stylów i zewnętrzne fonty opóźniały pierwsze renderowanie o 600 ms. Osadziliśmy CSS na stronie, a fonty przenieśliśmy na własny serwer. Po trzecie, 6 z 24 ostrzeżeń o kontraście było fałszywych. Pełne skanowanie wykryło potem 13 stron pominiętych w próbce.

Ta strona nie używa frameworka. Skrypty Python generują zwykły HTML, style mieszczą się w jednym pliku CSS, a JavaScript w trzech małych plikach. Obrazy powstają w WebP, w trzech rozmiarach. Wszystkie oczywiste poprawki wydajności były już gotowe.

Wynik mobilny utknął na 82.

Po usunięciu oczywistych błędów brakujące punkty nie kryły się w jednej dużej usterce. Rozłożyły się na trzy miejsca. Żeby znaleźć je wszystkie, trzeba było zrozumieć sens ostrzeżenia, a nie tylko je przeczytać. co ma na myśli wymagało zrozumienia.

Pierwsze miejsce: sama kontrola napisana po to, by pominąć pracę

Ostrzeżenie Lighthouse „wymuszone przeliczanie układu” (forced reflow) oznacza, że JavaScript każe przeglądarce zbyt wcześnie obliczyć układ strony. Przeglądarka zbiera zmiany stylów i stosuje je naraz. Gdy kod wcześniej odczyta wymiary, musi od razu przeliczyć zaległe zmiany.

Ostrzeżenie wskazywało linię w kodzie, ale w tej linii nie było widać żadnego pomiaru. Znaleźliśmy go po trzech podejściach.

  • W pierwszej rundzie getComputedStyle Usunęliśmy wywołania. Wynik bez zmian.
  • W drugiej rundzie wyszedł prawdziwy winowajca: napisaliśmy kod, który miał pomijać zbędną pracę if (window.scrollY > 0) sprawdzenie. Ono samo odczytywało układ. Wiersz, który miał pominąć tę pracę, właśnie ją uruchamiał.
  • W trzeciej rundzie usunęliśmy funkcję. Stan początkowy definiował już CSS, więc JavaScript nie musiał tworzyć go ponownie.

Został jeszcze jeden schemat void element.offsetWidth było. To popularny i uznany trik, który celowo wymusza układ, by ponownie uruchomić przejście. Zastąpiły go dwa kolejne requestAnimationFrame wstawiliśmy go. Robi to samo i nie rozpycha układu.

function uruchomPonownie(fn) { /* Gdy karta jest w tle, rAF nie ruszy i demo zostanie puste. */ if (document.hidden) { fn(); return; } requestAnimationFrame(function () { requestAnimationFrame(fn); }); }
Uruchom przejście ponownie bez wymuszania układu. Kod musi mieć wyjście awaryjne dla karty w tle. rAF w ogóle się tam nie uruchamia.

Ten przypadek nauczył nas jednego: ostrzeżenie o wydajności często nie mówi „robisz za dużo”, lecz „robisz to w złym momencie”.

Drugie miejsce: 600 ms przed pierwszym renderowaniem

Drugie ostrzeżenie dotyczyło zasobów blokujących pierwsze wyświetlenie. Po otwarciu strony przeglądarka pobierała i przetwarzała najpierw arkusz stylów, potem fonty. Dopiero wtedy zaczynała rysować. Zmierzone opóźnienie wyniosło 600 milisekund.

Wpięliśmy plik stylów w stronę

Osobny plik CSS przy drugiej wizycie przychodzi z cache’u. To plus. Przy pierwszej dokłada jednak dodatkową rundę tam i z powrotem, a pierwsza wizyta to dokładnie to, co widzi ktoś z wyniku wyszukiwania. Podczas publikacji zmniejszamy CSS i osadzamy go w stronie. Zmierzyliśmy: po kompresji Brotli wersja osadzona była mniejsza niż osobny plik razem z drugim żądaniem.

Przenieśliśmy fonty na własny serwer

Setup z Google Fonts szedł do dwóch źródeł: po styl fonts.googleapis.com, dla plików fonts.gstatic.com. Każdy nowy host wymaga rozwiązania DNS, uzgodnienia TLS i kolejnego połączenia. Gdy go usunęliśmy, pomiar pokazał:

250 KB165 KB

Tyle danych fontów pobiera strona po turecku. Zamiast 6 plików z 2 zewnętrznych źródeł ma 3 pliki z jednego źródła.

Jak to zmierzyliśmy CSS Google Fonts pobraliśmy z prawdziwą tożsamością Chrome, unicode-range Na podstawie pól wydzieliliśmy podzbiory, które strona po turecku naprawdę pobierze (latin + latin-ext), i zmierzyliśmy rozmiar każdego pliku. Druga strona z tego repozytorium assets/fonts rzeczywisty rozmiar plików.

Osobno opisaliśmy, jak kurczyły się fonty i gdzie płacisz cenę, gdy otwierasz projekt na dziesięć języków: dziesięć języków, trzy pliki fontów.

Trzecie miejsce: tu narzędzie się myli

Test dostępności zgłosił 24 ostrzeżenia o kontraście kolorów. Gdybyśmy poprawili je bez sprawdzania, zepsulibyśmy połowę projektu. 6 ostrzeżeń było błędnych.

Powód content-visibility:auto było. Ta funkcja odkłada renderowanie fragmentów poza ekranem i przyspiesza pierwsze wyświetlenie. Dokładnie o to nam chodziło. Obliczony kolor odłożonego fragmentu nie trafiał jednak do narzędzia audytowego, więc zgłaszało „białe na białym”. Na ekranie tego problemu nie było.

Sprawdziliśmy każdą proporcję w przeglądarce. 18 ostrzeżeń było trafnych, więc je poprawiliśmy. 6 wynikało ze ślepej plamki narzędzia. content-visibility usunęliśmy go tylko z dwóch wadliwych sekcji. W pozostałych został.

Ostrzeżenie z audytu nie dowodzi, że masz błąd do naprawy. Traktuj je jak tezę. Najpierw ją sprawdź, potem popraw kod. Na tej stronie ta lekcja wróciła jeszcze dwa razy. Jeden z tych przypadków to w notatce o korekcie językowej opisujemy.

I rana, którą sami sobie zadaliśmy

Aby uniknąć przesunięć układu, wszystkim obrazom width i height Dodaliśmy atrybut. Dobry ruch. Przeglądarka rezerwuje miejsce na obraz bez pobierania go. Ale boksy demo się rozsypały: część grafik narysowała się na 58 pikseli szerokości i 1500 pikseli wysokości.

Powód: w HTML width i height atrybuty podpowiadają układ, a w CSS height uzupełniają jego wartość. W projekcie dla tych pól tylko aspect-ratio kod określał, height było puste. atrybut trafił właśnie tam i nadpisał proporcje. Poprawka w jednej linijce:

img{max-width:100%;height:auto;display:block}
Atrybuty zostają na miejscu i dalej zatrzymują przesunięcie układu. Wysokość odbiera CSS.

I tu skanowanie naprawdę pomaga

Gdy strona główna miała 100, temat wyglądał na zamknięty. Nie był. Zmierzyliśmy próbkę. Witryny wszystkie 150 stron Przepuściliśmy je po kolei przez Lighthouse. Wynik: 137 stron miało 100 w czterech kategoriach, nie 13 stron. Dostępność, dobre praktyki i SEO miały 100 na każdej stronie. Odstawała tylko wydajność.

11 z 13 stron było w tym samym języku: po arabsku. Najniższy wynik: 83.

/ar/safecoast/ 83 /ar/laya/ 84 /ar/localmind/ 84 /ar/adforge/ 85 /ar/garanti/ 85 /ar/gtin/ 86 /ar/visionguard/ 86 /ar/ 89 /ar/growthos/ 97 /ar/dream/ 99 /ar/twin/ 99
Strony arabskie po pełnym skanowaniu. Dwa pozostałe odstępstwa (/pl/modelmesh/ i /ru/terms.html, oba 98) mieszczą się w zwykłych wahaniach między pomiarami.

Lighthouse wskazał powód wprost: Font wczytany → ar.woff2. Arabski font ładował się za późno i układał stronę od nowa. Metryki arabskiego fontu zapasowego mocno różniły się od Noto Sans Arabic, więc skok był duży. Poprawka wynikała z decyzji, którą podjęliśmy już przy Martian Mono: zamień nie optional.

0,2870

Przesunięcia układu na stronach arabskich (CLS)

Jak to zmierzyliśmy Po zmianie zmierzyliśmy ponownie 15 stron arabskich: każda miała CLS równe zero. Wyniki wydajności wzrosły z 83-89 do 98-100, a 9 z 15 stron dostało pełne 100. Pomiar zrobiliśmy w Lighthouse na działającej stronie.

Sedno nie dotyczy wydajności. Gdy bierzesz próbkę i mówisz „strona ma 100”, składasz obietnicę na temat strony, której nie zmierzyłeś. Piętnaście stron stało tak miesiącami z wynikiem 83, bo nikt do nich nie zajrzał.

Granica wyniku

Sam wynik pochodzi z laboratorium. Zakłada stałe urządzenie i model sieci. Telefon oraz zasięg prawdziwego użytkownika mogą działać gorzej. W kolejnych pomiarach tej samej strony wynik waha się od 98 do 100. Wynik 100 nie oznacza „szybko dla każdego”, lecz „usunęliśmy wszystkie mierzalne przeszkody”. Gdy zbierzemy dane prawdziwych użytkowników, sprawdzimy dane terenowe Chrome.

Wspólny mianownik tych trzech poprawek: żadna nie polegała na dokładaniu pracy. Usunęliśmy pracę wykonywaną w złym momencie albo taką, której nie trzeba było robić.

Częste pytania

Co Lighthouse rozumie przez „wymuszone przeliczanie układu”?

JavaScript zmusza przeglądarkę, by przed czasem obliczyła nagromadzone zmiany stylów. Przeglądarka zbiera je i stosuje za jednym razem. Jeśli kod wcześniej odczyta wymiary, musi natychmiast przeliczyć całą kolejkę. offsetWidth, getBoundingClientRect, scrollY i getComputedStyle każdy odczyt to uruchamia.

Czy zawsze warto osadzać CSS w stronie?

Nie. Osobny plik przy drugiej wizycie przychodzi z cache’u. To plus. Osadzenie przyspiesza pierwszą wizytę i spowalnia kolejne. Dobierz to do strony: gdy ruch przychodzi głównie z wyników wyszukiwania, osadzenie daje zysk. U nas pomiary to potwierdziły.

Czy mam przestać używać content-visibility?

Nie. Odkłada renderowanie sekcji poza ekranem, więc pierwsze wyświetlenie naprawdę przyspiesza. Problem leży w audytorze, który nie potrafi odczytać koloru odroczonej sekcji. Usuń tę opcję wyłącznie z sekcji błędnie oznaczonych w raporcie.

Czy 100 punktów naprawdę oznacza szybką stronę?

Nie. To pomiar laboratoryjny, który zakłada stałe urządzenie i stałą sieć. Wynik tej samej strony mierzony kilka razy z rzędu waha się od 98 do 100. Rzeczywisty obraz znajdziesz w danych terenowych Chrome. Ich zebranie wymaga czasu.

Dlaczego masz mierzyć wszystkie strony zamiast próbki?

U nas 13 ze 150 stron w ogóle nie weszło do próby, a 11 było w tym samym języku. Strona główna miała 100, a arabskie strony stały na 83. Nie oceniasz strony, której nie mierzysz.

Źródła

Powiązane projekty