Unikaj dużych i złożonych układów oraz rzucania niepotrzebnych elementów

Data publikacji: 20 marca 2015 r., ostatnia aktualizacja: 7 maja 2025 r.

W ramach układu przeglądarka określa informacje geometryczne elementów: ich rozmiar i położenie na stronie. Każdy element będzie miał jawne lub domyślne informacje o rozmiarach na podstawie użytego arkusza CSS, zawartości elementu lub elementu nadrzędnego. W Chrome (i wywodzonych z niego przeglądarkach, takich jak Edge) oraz w Safari proces ten nazywa się układ. W Firefoksie nazywa się to „przepływem”, ale proces jest w zasadzie taki sam.

Podobnie jak w przypadku obliczeń dotyczących stylu, najważniejsze kwestie dotyczące kosztów układu to:

  1. Liczba elementów wymagających układu, która jest pochodną wielkości DOM strony.
  2. złożoność tych układów;

Podsumowanie

  • Układ ma bezpośredni wpływ na opóźnienie interakcji
  • Układ jest zwykle ograniczony do całego dokumentu.
  • Liczba elementów DOM wpływa na wydajność. W miarę możliwości należy unikać uruchamiania układu.
  • Unikaj wymuszania układów synchronicznych i zbędowania układu; odczytaj wartości stylów, a potem wprowadź zmiany w stylach.

Wpływ układu na opóźnienie interakcji

Gdy użytkownik wchodzi w interakcję ze stroną, interakcje te powinny przebiegać możliwie szybko. Czas, jaki zajmuje wykonanie interakcji (kończy się, gdy przeglądarka wyświetli następny element, aby pokazać wyniki interakcji), to opóźnienie interakcji. Jest to jeden z aspektów wydajności strony, który mierzy wskaźnik interakcja do kolejnego wyrenderowania.

Czas, jaki zajmuje przeglądarce wyświetlenie kolejnego klatki w odpowiedzi na interakcję użytkownika, nazywany jest opóźnieniem wyświetlania. Celem interakcji jest dostarczenie wizualnej informacji zwrotnej, aby zasygnalizować użytkownikowi, że coś się stało. Aby to osiągnąć, konieczne może być wprowadzenie pewnych zmian w układzie.

Aby jak najbardziej obniżyć INP witryny, należy unikać układu, jeśli to możliwe. Jeśli nie można całkowicie uniknąć układu, należy ograniczyć jego działanie, aby przeglądarka mogła szybko wyświetlić następny kadr.

.

Unikaj układu, o ile to możliwe

Gdy zmienisz style, przeglądarka sprawdza, czy któreś z tych zmian wymagają obliczenia układu i zaktualizowania drzewa renderowania. Zmiany „właściwości geometrycznych”, takich jak width, height, left lub top, wymagają układu.

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

Układ jest prawie zawsze ograniczony do całego dokumentu. Jeśli masz dużo elementów, ustalenie ich lokalizacji i wymiarów może zająć dużo czasu.

Jeśli nie można uniknąć układu, należy ponownie użyć narzędzi programistycznych Chrome, aby sprawdzić, ile czasu zajmuje działanie, i określić, czy układ jest przyczyną wąskiego gardła. Najpierw otwórz Narzędzia deweloperskie, przejdź na kartę Czas i kliknij przycisk nagrywania, a potem wejdź w interakcję ze swoją witryną. Po zakończeniu nagrywania zobaczysz podsumowanie wydajności witryny:

Narzędzia programistyczne z wieloma fioletowymi blokami układu.
Narzędzie DevTools w Chrome pokazuje długi czas w sekcji Layout.

Po zapoznaniu się z wykresem z poprzedniego przykładu widzimy, że na układ w przypadku każdego klatki zużywa się ponad 28 ms, co jest zbyt dużo, biorąc pod uwagę, że na wyświetlenie klatki w ramach animacji mamy 16 ms. Widzisz też, że DevTools podaje rozmiar drzewa (w tym przypadku 1618 elementów) oraz ile węzłów wymaga układu (w tym przypadku 5).

Pamiętaj, że ogólna rada brzmi: unikaj układu w miarę możliwości, ale nie zawsze jest to możliwe. W przypadkach, gdy nie możesz uniknąć układu, pamiętaj, że jego koszt jest powiązany z rozmiarem DOM. Chociaż związek między tymi dwoma parametrami nie jest ścisły, większe DOM-y będą zazwyczaj generować wyższe koszty układu.

Unikaj wymuszania układów synchronicznych

Przesyłanie ramki do ekranu odbywa się w tej kolejności:

Używanie flexbox jako układu.
Kroki renderowania

Najpierw wykonywany jest kod JavaScript, potem obliczenia stylu, potem układ. Można jednak zmusić przeglądarkę do wykonania układu wcześniej za pomocą JavaScriptu. Jest to tzw. wymuszony układ synchroniczny (czasami nazywany wymuszonym reflowem).

Należy pamiętać, że podczas wykonywania kodu JavaScript wszystkie stare wartości układu z poprzedniego elementu są znane i dostępne do zapytania. Jeśli np. chcesz zapisać wysokość elementu (nazwijmy go „pudełko”) na początku klatki, możesz napisać taki kod:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Problemy mogą pojawić się, jeśli zmienisz styl pudełka przed określeniem jego wysokości:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Aby odpowiedzieć na pytanie o wysokość, przeglądarka musi najpierw zastosować zmianę stylu (z powodu dodania klasy super-big), a potem uruchomić układ. Dopiero wtedy będzie można zwrócić prawidłową wysokość. To zbędna i potencjalnie kosztowna praca.

Z tego powodu zawsze wykonuj zbiorcze odczyty stylów i najpierw je wykonuj (gdzie przeglądarka może używać wartości układu z poprzedniego kadru), a potem wykonaj wszystkie zapisy:

Bardziej wydajna wersja poprzedniej funkcji:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

W większości przypadków nie trzeba stosować stylów, a potem wartości zapytań. Wystarczy użycie wartości z ostatniego kadru. Wykonywanie obliczeń stylów i układów w sposób synchroniczny oraz wcześniej niż to robi przeglądarka może być potencjalnym punktem wąskim, więc zazwyczaj nie jest to pożądane działanie.

Unikaj twardego przełączania układu

Istnieje sposób na jeszcze gorsze wymuszanie układów synchronicznych: wykonywanie ich w dużej liczbie w krótkich odstępach czasu. Spójrz na ten kod:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

Ten kod przechodzi przez grupę akapitów i ustawia szerokość każdego akapitu tak, aby pasowała do szerokości elementu o nazwie „box”. Wygląda to niewinnie, ale problem polega na tym, że każda iteracja pętli odczytuje wartość stylu (box.offsetWidth), a następnie od razu użyje jej do zaktualizowania szerokości akapitu (paragraphs[i].style.width). W następnej iteracji pętli przeglądarka musi wziąć pod uwagę fakt, że od ostatniego wywołania funkcji offsetWidth (w poprzedniej iteracji) style uległy zmianie, więc musi zastosować zmiany stylu i uruchomić układ. Będzie to miało miejsce przy każdej iteracji.

Aby rozwiązać ten problem, ponownie odczytaj, a potem zapisz wartości:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

Identyfikowanie wymuszonych układów synchronicznych i throttlingu

W DevTools jest funkcja Forced Reflow (Wymuszone przepływanie), która pomaga szybko wykrywać przypadki wymuszonych układów synchronicznych (zwanych też „wymuszonym przepływaniem”).

Narzędzia deweloperskie wyświetlające informacje o wymuszonym przepływie danych, które wskazują na funkcję o nazwie „w”, która powoduje wymuszone przepływanie.
Informacje o wymuszonym przepływie danych w Narzędziach deweloperskich w Chrome.

Wymuszone układy synchroniczne można też zidentyfikować w polu za pomocą przypisu skryptu Long Animation Frame API za pomocą właściwości forcedStyleAndLayoutDuration.