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

Układ to na nim przeglądarka określa informacje geometryczne o elementach – ich rozmiarze i położeniu na stronie. Każdy element będzie miał jawne lub domyślne informacje o rozmiarze na podstawie użytego kodu CSS, zawartości elementu lub elementu nadrzędnego. Ten proces nazywa się układem w Chrome.

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 zawierać wyraźne lub niejawne informacje o rozmiarze ustalone na podstawie użytego kodu CSS, zawartości elementu lub elementu nadrzędnego. Ten proces nazywa się Układ w Chrome (i w przeglądarkach pochodnych, takich jak Edge) i Safari. W Firefoksie nazywa się to Reflow, ale sposób działania jest w praktyce 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ą rozmiaru 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ędnego stosowania układów; odczytaj wartości stylów, a następnie wprowadź zmiany w stylach.

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

Interakcje użytkownika ze stroną powinny przebiegać możliwie szybko. Czas, jaki zajmuje interakcja (kończy się, gdy przeglądarka wyświetli kolejny kadr, 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 przeglądarka potrzebuje na wyświetlenie kolejnego klatki w odpowiedzi na interakcję użytkownika, nazywany jest opóźnieniem wyświetlania. Celem interakcji jest przekazanie informacji wizualnych, aby zasygnalizować użytkownikowi, że coś się stało, a aktualizacje wizualne mogą wymagać trochę pracy związanej z układem strony, by osiągnąć ten cel.

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 sprawdzi, czy któreś z tych zmian wymagają obliczenia układu i zaktualizowanie tego drzewa renderowania. Zmiany „właściwości geometrycznych”, takich jak szerokość, wysokość, lewo lub prawo, wymagają użycia 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, trzeba jeszcze raz użyć Narzędzi deweloperskich w Chrome, aby sprawdzić, jak długo to zajmuje i ustalić, czy przyczyną wąskiego gardła jest układ. Najpierw otwórz Narzędzia deweloperskie, przejdź na kartę Czas i kliknij przycisk nagrywania, a potem wejdź w interakcję ze swoją witryną. Po zatrzymaniu nagrywania zobaczysz zestawienie skuteczności witryny:

Narzędzia deweloperskie wyświetlające długi czas w sekcji Layout.

Zbadaj ślady w tym przykładzie, że każda klatka w układzie trwa ponad 28 milisekund, co oznacza, że jeśli mamy 16 milisekund na ukazanie klatki na ekranie w animacji, jest to znacznie za dużo. Widać też,że z Narzędzi deweloperskich dowiesz się, jaki jest 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.

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.

Pamiętaj, że podczas wykonywania kodu JavaScript wszystkie stare wartości układu z poprzedniego elementu są znane i dostępne do zapytania. Jeśli na przykład 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ść. Jest to zbędna i potencjalnie kosztowna praca.

Z tego względu należy zawsze zgrupować odczyty stylu i wykonać je najpierw (gdzie przeglądarka może użyć wartości układu poprzedniej ramki), a potem wykonać wszelkie zapisy:

Jeśli powyższa funkcja została wykonana poprawnie, wyglądałaby tak:

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

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

Na ogół nie musisz stosować stylów, a później wysyłać zapytań o wartości. Wystarczy, że użyjesz wartości ostatniej klatki. Wykonywanie obliczeń stylów i układów w sposób synchroniczny oraz wcześniej niż chce tego przeglądarka może być potencjalnym punktem wąskim i nie jest to coś, czego zwykle się chce.

Unikanie thrashingu 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`;
  }
}

Kod zapętla grupę akapitów i ustawia szerokość każdego akapitu na szerokość 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) zmieniły się style, 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`;
  }
}

Jeśli chcesz mieć pewność, że wszystko będzie działać prawidłowo, użyj FastDOM, który automatycznie grupował będzie Twoje operacje odczytu i zapisu. Dzięki temu nie będziesz przypadkowo uruchamiać wymuszonych synchronicznych układów ani niepotrzebnie nie uruchamiać funkcji layout thrashing.