Przepływ danych, ponowne malowanie, kompozyt: ile kosztuje przeglądarce każda zmiana w CSS
Prześledź proces działania HTML i CSS w ramach DOM, CSSOM, układu elementów, procesu malowania oraz kompozycji obrazu, i dowiedz się, dlaczego zmiany szerokości są kosztowniejsze od zmian kolorów oraz jak uniknąć problemów związanych z szybkim przestrajaniem układu.
Źródła: Browser Rendering Pipeline Explained: From HTML to Pixels + Reflow vs Repaint
Jedna linia kodu znacznikowego, takiej jak nagłówek, wydaje się błahe, ale przeglądarka wykonuje długą serię operacji, zanim element stanie się pikselami na ekranie. Znajomość tej sekwencji pozwala odpowiadać na praktyczne pytania: dlaczego zmiana rozmiaru elementu może spowolnić stronę, dlaczego animacje zazwyczaj są tworzone za pomocą transform, dlaczego jedna zmiana może wpłynąć na dziesiątki innych elementów oraz dlaczego intensywna manipulacja DOM powoduje problemy z działaniem interfejsu. Ten przewodnik omawia każdy etap procesu renderowania, a następnie wykorzystuje ten model do wyjaśnienia różnicy między reflow a repaint oraz nawyków, które zapewniają szybką pracę interfejsu.
<h1>Hello World</h1>
Przegląd procesu renderowania
W istocie proces renderowania to procedura, która przekształca trzy rodzaje danych wejściowych – kod znacznikowy, style i skrypty – w obraz, który może zostać wyświetlony.
HTML + CSS + JavaScript
↓
Pixels
↓
Screen
Jeśli rozłożyć to na poszczególne etapy, sekwencja wygląda mniej więcej w ten sposób:
Parse HTML
↓
Build DOM
↓
Parse CSS
↓
Build CSSOM
↓
Create Render Tree
↓
Calculate Styles
↓
Layout / Reflow
↓
Paint / Repaint
↓
Composite
↓
Pixels on Screen
Gdy ludzie mówią o szybkości, z jaką strona pokazuje swój pierwszy widoczny treść, zwykle nazywają tę sekwencję Krytyczną ścieżką renderowania. Należy pamiętać, że jest to model, a nie specyfikacja konkretnego silnika. Chrome, Firefox i Safari implementują te etapy w różny sposób, a w późniejszych aktualizacjach pomijają je, gdy tylko mogą udowodnić, że ich wykonanie jest niepotrzebne. Model ten pozostaje na tyle dokładny, by umożliwić ocenę wydajności, co jest kluczowe w codziennej pracy z interfejsem użytkownika.
Etap 1: parsowanie HTML do DOM
Wszystko zaczyna się od dokumentu HTML zwracanego przez serwer – na przykład mała strona z jednym nagłówkiem i jednym akapitem:
<!DOCTYPE html>
<html>
<body>
<h1>Hello World</h1>
<p>Welcome to my website.</p>
</body>
</html>
Parser czyta ten kod i tworzy Document Object Model – drzewo węzłów, w którym każdy element oraz każdy fragment tekstu ma swoje miejsce. Przedstawione w formie graficznej drzewo powyższego dokumentu wygląda następująco:
HTML
<html>
│
└── body
│
├── h1
│ └── "Hello World"
│
└── p
└── "Welcome..."
DOM rejestruje zawartość dokumentu oraz sposób jego ułożenia w strukturze hierarchicznej. Jest to również interfejs, z którym pracują skrypty, więc wywołanie takie jak poniżej przemierza to drzewo w celu znalezienia odpowiedniego węzła:
document.querySelector("h1");
Proces analizy kodu odbywa się stopniowo. Przeglądarka nie czeka na cały plik, zanim zacznie tworzyć węzły, dlatego długa strona może zacząć się renderować, podczas gdy pozostała część nadal jest przesyłana.
Etap 2: analiza CSS do CSSOM
Sama struktura nie mówi nic o wyglądzie. Do tego celu przeglądarka potrzebuje plików stylów, takich jak te dwa proste reguły:
h1 {
color: blue;
font-size: 32px;
}
p {
color: gray;
}
CSS jest analizowany i przekształcany w drugą strukturę, mianowicie CSS Object Model. Przydatnym sposobem na rozdzielenie tych dwóch struktur jest pytanie, na które każda z nich odpowiada:
DOM
↓
"What exists on the page?"
CSSOM
↓
"How should those things look?"
Aby wyświetlić treść, potrzebne są odpowiedzi na oba te pytania. To również powód, dla którego pliki stylów mogą blokować proces renderowania.
Etap 3: łączenie obu struktur w drzewo renderowania
Gdy DOM i CSSOM są już dostępne, przeglądarka łączy je w drzewo renderowania.
DOM
+
CSSOM
↓
Render Tree
Drzewo renderowania zawiera jedynie to, co faktycznie zostanie narysowane, wraz ze stylami, które do niego odnoszą się. Weźmy ten fragment:
<div>
<h1>Hello</h1>
<p>World</p>
</div>
Po połączeniu można to wyobrazić sobie jako drzewo widocznych prostokątów, które przechowują informacje o swoich stylach:
Render Tree
div
│
├── h1
│ ├── font-size: 32px
│ └── color: blue
│
└── p
└── color: gray
Wszystko, co nie bierze udziału w renderowaniu, jest pomijane. Element z display: none w ogóle nie ma żadnej pozycji, co różni się od visibility: hidden: ukryty element nadal zajmuje przestrzeń, więc pozostaje w drzewie i nadal uczestniczy w układzie.
Etap 4: obliczanie stylów obliczonych
Następnie przeglądarka oblicza ostateczny, styl obliczony dla każdego elementu, który ma zostać wyświetlony. Prawdziwe pliki stylów zawierają takie nakładające się reguły:
.card {
width: 300px;
}
.card p {
font-size: 16px;
}
Dla każdego elementu silnik musi odpowiedzieć kolejno na trzy pytania:
Which rules apply?
↓
Which rule wins?
↓
What is the final computed style?
Odpowiedź na nie oznacza zastosowanie wszystkich reguł rozstrzygania w CSS:
- kaskada
- dziedziczenie od elementów rodzicielskich
- spezyficzność selektorów
- przekształcenie na wartości obliczone
- style zależne od zapytań mediów
Wynikiem jest jeden zestaw rozwiązanych właściwości na każdy element:
DOM + CSSOM
↓
Style Calculation
↓
Computed Styles
Etap 5: układ, w którym określana jest geometria
W tym momencie przeglądarka wie, co ma pokazać i jak powinno to wyglądać. Nie wie jeszcze jednak, gdzie znajdzie się każda część i jaki będzie jej rozmiar. Odpowiedzią na to pytanie jest układ, który przekształca obszar wyświetlania w zbiór ułożonych pudełek:
Viewport
┌────────────────────────────┐
│ │
│ Header │
│ │
├──────────────┬─────────────┤
│ Sidebar │ Content │
│ │ │
│ │ │
└──────────────┴─────────────┘
Podczas układu silnik oblicza dla każdego pudełka w drzewie renderowania:
- szerokość i wysokość
- pozycję
- marże i odstępy
- relację każdego pudełka z sąsiadami i przodkami
- ogólną geometrię strony
MDN podsumowuje ten etap jako określanie rozmiaru i lokalizacji każdego węzła w drzewie renderowania. Ponieważ pudełka są ze sobą powiązane, układ to problem dotyczący całego drzewa, a nie pojedynczych elementów.
Reflow: ponowne utworzenie układu
Pierwszy etap przetwarzania geometrii nazywany jest po prostu układem. Każdy późniejszy etap wywołany zmianą zazwyczaj określany jest jako reflow, a niektóre zespoły używają potocznego terminu relayout dla tego samego zjawiska:
Layout
↓
Initial calculation
Reflow / ReLayout
↓
Subsequent layout calculation
W pozostałej części tego przewodnika używany jest termin reflow, ponieważ to właśnie ten termin pojawia się w większości dyskusji na temat wydajności oraz w narzędziach do jej optymalizacji.
Co wywołuje reflow
Zacznijmy od prostokąta o stałej szerokości:
.box {
width: 200px;
}
Teraz skrypt zmienia tę szerokość w czasie wykonywania programu:
box.style.width = "500px";
To jedno przypisanie rodzi szereg pytań, na które przeglądarka musi odpowiedzieć, zanim będzie mogła narysować następny ramek:
The box changed width.
What else is affected?
Does its parent change?
Do neighboring elements move?
Does the text wrap differently?
Does the height change?
Does the entire layout need recalculation?
Dlatego część lub cała strona może wymagać nowego etapu utworzenia układu, po którym uruchamiają się również kolejne kroki przetwarzania:
Change width
↓
Style recalculation
↓
Layout / Reflow
↓
Paint
↓
Composite
Właściwości wpływające na geometrię
Każda właściwość, która wpływa na rozmiar lub pozycję, może powodować konieczność przeliczenia układu. Najczęściej winowajcami są:
width
height
margin
padding
top
left
right
bottom
font-size
Zmiana jednej z nich może przesunąć lub zmienić rozmiar samego elementu, a potencjalnie także wszystkiego wokół niego. Na przykład obie poniższe aktualizacje mogą zmusić przeglądarkę do ponownego obliczenia układu:
element.style.width = "500px";
element.style.marginLeft = "100px";
font-size powinien znaleźć się na liście, ponieważ zmiana rozmiaru tekstu wpływa na wysokość wiersza i jego rozmieszczenie, co z kolei zmienia wysokość bloku zawierającego go oraz pozycję wszystkich elementów po nim.
Dlaczego przeliczanie układu jest kosztowne
Rozważmy typową strukturę strony z kilkoma obszarami i zawartym w nich treścią:
Page
│
├── Header
├── Navigation
├── Sidebar
├── Main Content
│ ├── Card
│ ├── Card
│ ├── Card
│ └── Table
└── Footer
Jeśli element znajdujący się blisko góry stanie się wyższy, każda karta, tabela oraz stopka poniżej niego mogą potrzebować nowych współrzędnych. Jedna zmiana geometryczna może wpłynąć na dużą część struktury:
One Geometry Change
↓
Layout Recalculation
↓
Many Elements Recalculated
↓
Paint
↓
Composite
Taka rozgałęziona struktura sprawia, że niepotrzebne prace związane z układem są kosztowne, szczególnie gdy powtarzają się podczas animacji lub przeciągania.
Etap 6: rysowanie
Gdy tylko ustalone zostaną trzy elementy, przeglądarka może zacząć tworzyć piksele:
What to display
+
Where to display it
+
How it should look
Rysowanie przekształca umieszczone elementy w operacje rysunkowe, a ostatecznie w piksele. Zawartość narysowana obejmuje:
- tekst
- kolory i tła
- obwody
- cienie
- obrazy
- wszelkie inne widoczne elementy dekoracyjne
Layout
↓
Paint
↓
Pixels
MDN opisuje rysowanie jako etap, w którym elementy przekształcają się z formy ustalonej przez układ w rzeczywiste piksele na ekranie.
Ponowne rysowanie: nowe piksele, ta sama geometria
Ponowne rysowanie jest konieczne, gdy element wygląda inaczej, ale zajmuje dokładnie tę samą przestrzeń. Klasycznym przykładem jest zmiana koloru tekstu:
color: red;
Nowa kolor nie ma żadnego wpływu na nic z poniższych elementów:
Width
Height
Position
Layout relationships
Dlatego przeglądarka może całkowicie pominąć proces układu i wykorzystać krótszą ścieżkę:
Style Recalculation
↓
Paint / Repaint
↓
Composite
Reflow versus repaint obok siebie
To rozróżnienie stanowi istotę większości zaleceń dotyczących wydajności renderowania, dlatego warto dokładnie opisać obie strony.
Reflow przelicza geometrię
Reflow oznacza konieczność ponownego obliczenia rozmiaru lub pozycji co najmniej jednego elementu. Standardowym przykładem jest ustawienie szerokości:
element.style.width = "500px";
Następnie uruchamiana jest cała kolejność etapów:
Change
↓
Style
↓
Layout / Reflow
↓
Paint
↓
Composite
Repaint jedynie zmienia wygląd
Repaint oznacza, że element wygląda inaczej, ale nie jest konieczne ponowne obliczanie geometrii. Wystarczy ustawienie koloru, aby to spowodować:
element.style.color = "red";
Proces układu jest pomijany w tej kolejności:
Change
↓
Style
↓
Paint / Repaint
↓
Composite
Każdy z nich sprowadza się do jednej linijki:
RELayout / Reflow
→ Geometry changes
Repaint
→ Visual appearance changes
Przycisk, który zmienia szerokość lub kolor
Załóżmy prosty przycisk zachęcający do działania:
┌─────────────────────┐
│ Buy Now │
└─────────────────────┘
Załóżmy, że jego szerokość zostanie zmieniona z pierwszej wartości na drugą:
width: 200px;
width: 400px;
Przycisk zajmuje teraz dwa razy większą przestrzeń poziomą, co może odsunąć inne elementy na bok lub spowodować przełamanie wiersza. To zmiana geometryczna, więc przeglądarka ponownie układa elementy, a następnie je rysuje:
Width changes
↓
Geometry changes
↓
Reflow
↓
Repaint
Załóżmy teraz, że zmienia się tylko kolor tła:
background-color: blue;
Przycisk zachowuje swoje dokładne wymiary i pozycję, a zmieniają się tylko jego piksele:
Color changes
↓
Geometry unchanged
↓
Repaint
Związek jest jednokierunkowy
Gdy zmienia się układ, elementy trafiają w nowe miejsca, a te nowe miejsca wymagają nowych pikseli:
Layout changes
↓
Browser needs new pixels
↓
Paint
Krótko mówiąc, po ponownym układzie elementów zwykle następuje ich ponowne narysowanie:
Reflow
↓
Usually followed by
↓
Repaint
Odwrotność nie jest prawdziwa. Ponowne narysowanie może nastąpić samo z siebie, bez żadnej pracy nad układem:
Repaint
↓
Does NOT necessarily require
↓
Reflow
Etap 7: komponowanie warstw w ramkę
Współczesne silniki nie zawsze rysują całą stronę jako jedno płaskie obrazowanie. Mogą podzielić treść na oddzielne warstwy i połączyć je w końcowym kroku komponowania:
Layer 1
+
Layer 2
+
Layer 3
↓
Composite
↓
Final Frame
Strona może mieć na przykład warstwę tła, kartkę na własnej warstwie oraz element animowany na trzeciej warstwie:
┌──────────────────────┐
│ Background Layer │
│ │
│ ┌──────────────┐ │
│ │ Card Layer │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ Animation │ │
│ └──────────────┘ │
└──────────────────────┘
Komponentor układa te warstwy, aby stworzyć każdą ramkę. Niektóre elementy mogą zostać przeniesione na własną warstwę, a w odpowiednich przypadkach przeglądarki przekazują zadanie komponowania do GPU. Zmiana, która jedynie przesuwa lub łączy istniejącą warstwę, może być zatem zastosowana bez ponownego rysowania jej zawartości.
Dlaczego transform jest domyślnym rozwiązaniem dla animacji
Oto efekt hover, który przesuwa prostokąt poprzez animację wartości left, zaczynając od zera:
.box {
left: 0;
}
i kończąc 100 pikseli po prawej stronie:
.box:hover {
left: 100px;
}
Ponieważ left stanowi część pozycji elementu w układzie, każdy krok animacji wymaga przeliczenia układu oraz ponownego narysowania. Ten sam ruch wizualny można osiągnąć za pomocą transformacji:
.box {
transform: translateX(0);
}
.box:hover {
transform: translateX(100px);
}
Transformacja przesuwa już narysowany element bez zmiany jego obszaru układu, dzięki czemu przeglądarka może często obsłużyć całą animację w komponatorze, bez konieczności przeliczania układu czy ponownego rysowania na każdym kroku. opacity ma podobne właściwości i jest zwykle wybierany do efektów zanikania. Traktuj to raczej jako silny standard niż bezwzględne prawo. To, czy transformacja faktycznie zostanie przetworzona w komponatorze, zależy od elementu, innych stylów zastosowanych do niego, sposobu rozdzielania warstw oraz konkretnego silnika przeglądarki.
Które etapy faktycznie są uruchamiane przez zmianę
W przypadku większości aktualizacji stylów praca rozprzestrzenia się wzdłuż jednej z dwóch ścieżek po ponownym obliczeniu stylów:
Change
↓
Recalculate Style
↓
┌───────┴───────┐
↓ ↓
Layout No Layout
↓ │
Paint │
↓ ↓
└───────┬───────┘
↓
Composite
↓
Screen
Im mniej kosztownych etapów dotyka częsta aktualizacja, tym łatwiej utrzymać płynną wyświetlanie. MDN opisuje ten sam proces: ponowne obliczenie stylów, następnie układ, potem malowanie oraz kompozycja tam, gdzie to konieczne. Zmiany w atrybutach transform lub opacity na warstwie skomponowanej mogą pomijać nawet etap malowania, który jest najtańszym ze wszystkich.
Budżet klatki: około 16,67 ms
Większość ekranów odświeża się mniej więcej 60 razy na sekundę, co daje określony czas na każdą klatkę:
1 second
÷
60 frames
≈
16.67ms per frame
Innymi słowy, aby utrzymać 60 FPS, przeglądarka ma mniej więcej tyle czasu na każdą klatkę:
16.67ms
To budżet jest dzielony pomiędzy wszystko, co musi się wykonać przed pokazaniem klatki: Twój JavaScript, obliczanie stylów, układ, rysowanie oraz własne zadania zarządzające przeglądarki. Gdy suma tych operacji przekroczy dostępny budżet, klatka zostaje pominięta, co powoduje problemy z płynnością wyświetlania:
16.67ms
↓
Exceeded
↓
Frame missed
↓
Jank
Efekt ten jest najbardziej widoczny w interakcjach, które generują klatki w sposób ciągły:
- przewijanie
- animacje
- przeciąganie i upuszczanie
- modyfikacja rozmiaru okna lub panelu
- paneli kontrolne aktualizujące wiele wartości jednocześnie
MDN podkreśla to samo: obliczanie stylów, układ i rysowanie muszą mieścić się w dostępnym budżecie klatek, aby animacje wyglądały płynnie.
Problemy z układem: operacje wymuszające synchroniczny układ
Bardzo częsty sposób marnowania budżetu klatek wydaje się nieszkodliwy w kodzie. Poniższy fragment kodu ustawia szerokość, odczytuje rozmiar, ustawia wysokość i ponownie odczytuje:
element.style.width = "500px";
console.log(element.offsetWidth);
element.style.height = "500px";
console.log(element.offsetHeight);
Wzorzec, który to tworzy, polega na naprzemiennym wykonywaniu zapisów i odczytów:
Write
↓
Read layout
↓
Write
↓
Read layout
Zazwyczaj przeglądarka odkłada obliczanie układu do chwili, gdy ma narysować następny ramek, dzięki czemu kilka zapisów stylu może zostać przetworzonych w jednej rundzie. Jednak właściwości takie jak offsetWidth i offsetHeight muszą zwracać aktualne wartości. Odczyt ich tuż po zapisie zmusza silnik do synchronicznego, natychmiastowego wyeliminowania wszystkich zaległych operacji związanych ze stylem i układem. Jeśli robi się to wielokrotnie, często w pętli przeciągającej się nad wieloma elementami, powstaje problem z intensywnym obliczaniem układu: wiele pełnych rund obliczeń układu, podczas gdy wystarczyłaby tylko jedna.
Rozwiązaniem jest grupowanie operacji tak, aby wszystkie odczyty odbywały się najpierw, a wszystkie zapisy później:
Bad:
Write
Read
Write
Read
Write
Read
Better:
Read
Read
Read
Write
Write
Write
Sposób restrukturyzacji kodu zależy od aplikacji, ale zasada jest zawsze taka sama: nie zmuszaj przeglądarki do ponownego obliczania układu częściej, niż to konieczne. Blog przedstawia bardziej szczegółowe omówienie problemu częstego zmieniania układu oraz kolekcji DOM w czasie rzeczywistym, jeśli chcesz zobaczyć więcej przykładów przymusowych aktualizacji układu.
Praktyczne techniki dla tańszego renderowania
Animowanie transformacji i przeniknienia
Zamiast przesuwać element za pomocą właściwości układu w taki sposób:
left: 100px;
skorzystaj z transformacji, która pasuje do projektu:
transform: translateX(100px);
Aby stopniowo ukrywać i ponownie pokazywać element, zmieniaj się pomiędzy tymi dwoma wartościami:
opacity: 0;
opacity: 1;
zazwyczaj jest lepszą opcją niż animowanie właściwości, które wpływają na układ.
Zminimalizuj zmiany w DOM
Każda mutacja DOM może powodować dodatkową pracę dla przeglądarki. Dodawanie węzłów jeden po drugim w takiej sekwencji:
element.appendChild(...);
element.appendChild(...);
element.appendChild(...);
często lepiej zastąpić tworzeniem nowej treści poza dokumentem (na przykład DocumentFragment) i wstawieniem jej raz. Im większy i bardziej złożony jest DOM, tym droższe okazują się każde uniknione zmiany.
Aktualizacje grupowe zamiast ich mieszania
Kod, który naprzemiennie aktualizuje treść i zajmuje się układem, skutkuje sekwencją taką jak ta:
Update
↓
Layout
↓
Update
↓
Layout
↓
Update
↓
Layout
Celem jest dostarczenie przeglądarce grupy zmian, które może przetworzyć razem:
Update
Update
Update
↓
Browser processes the changes
Dla animacji i innych zadań wizualnych o wysokiej częstotliwości requestAnimationFrame() planuje wykonywanie aktualizacji tuż przed renderowaniem następnego klatki przez przeglądarkę, co zapewnia ich synchronizację z cyklem renderowania zamiast konfliktu z nim.
Nie animuj właściwości układu
Właściwości, które zmieniają geometrię, nie nadają się do wykonywania w każdym kadru:
width
height
margin
padding
top
left
right
bottom
font-size
Każda z nich może wywołać zmianę układu. Tam, gdzie to pozwala wynik wizualny, zamiast tego animuj te właściwości:
transform
opacity
Zarezerwuj miejsce na obrazy
Tag obrazu bez wymiarów nie daje przeglądarce żadnych wskazówek co do ilości wolnego miejsca:
<img src="image.jpg">
Gdy plik przybywa i staje się znana jego rzeczywista wielkość, otaczający go kontent przesuwa się, a układ jest ponownie aktualizowany. Deklaracja wielkości obrazu z góry zapobiega temu:
<img
src="image.jpg"
width="800"
height="600"
alt="Example"
/>
CSS aspect-ratio osiąga ten sam efekt w przypadku obrazów responsywnych. W obu przypadkach przeglądarka może zarezerwować odpowiedni obszar przed załadowaniem obrazu, co zapobiega widocznym zmianom w układzie.
Zmierz przed optymalizacją
Nie każda korekta wyglądu lub etap układu stanowi problem wymagający rozwiązania; silniki są mocno optymalizowane i szybko radzą sobie z większością tych zadań. Skup swoje wysiłki na dowodach za pomocą prostego pętli:
Performance Problem
↓
Measure
↓
Find Bottleneck
↓
Optimize
↓
Measure Again
Panel Performance w Chrome DevTools rejestruje chronologię, która pokazuje:
- wykonywanie JavaScriptu
- ponowne obliczanie stylów
- układ elementów
- rysowanie
- przetwarzanie obrazu
- długotrwałe zadania
- czas trwania na kadru
Napraw problem, który faktycznie ujawnia profil, a nie kod, który wydaje się kosztowny tylko przy jego lekturze.
Cały proces w jednym obrazku
Gdy wszystko jest połączone od początku do końca, ścieżka od źródła do ekranu wygląda tak:
HTML
│
↓
Parse HTML
│
↓
DOM
│
│
CSS ──→ Parse CSS ──> CSSOM
│
↓
DOM + CSSOM
│
↓
Render Tree
│
↓
Style Calculation
│
↓
Layout / Reflow
│
↓
Paint / Repaint
│
↓
Composite
│
↓
Pixels on Screen
A gdy coś się zmienia po pierwszym renderowaniu, kluczowym punktem decyzyjnym jest to, czy geometria została wpłynięta:
CSS / DOM Change
↓
Style Recalculation
↓
Does geometry change?
│
┌──┴──┐
YES NO
│ │
↓ ↓
Layout Paint
│ │
└──┬───┘
↓
Composite
↓
Screen
Główne wnioski
Trzy krótkie streszczenia obejmują to, co najważniejsze. Przepłynięcie treści to droga opcja:
RELayout / Reflow
↓
Changes geometry
↓
Width / Height / Position / Layout
↓
Usually leads to Paint
Przedrukowanie to ta tańsza opcja:
Repaint
↓
Changes appearance
↓
Color / Background / Shadow / etc.
↓
Doesn't necessarily require Layout
A jeśli chodzi o animacje, wybór właściwości w dużej mierze decyduje o koszcie:
Layout properties
↓
Potentially expensive
transform + opacity
↓
Often better for smooth animations
Nie musisz znać wewnętrznych mechanizmów każdego silnika, ale powinieneś być w stanie prześledzić ogólny proces:
HTML
↓
DOM
CSS
↓
CSSOM
DOM + CSSOM
↓
Render Tree
↓
Style
↓
Layout
↓
Paint
↓
Composite
↓
Pixels
Mając to na uwadze:
- Zmiany geometryczne, takie jak
width, powodują przepłynięcie treści, a następnie przedrukowanie; zmiany wyglądu, takie jakcolor, zazwyczaj wymagają tylko przedrukowania. transformiopacityczęsto mogą pominąć kroki układania i malowania, dlatego nadają się do animacji.- Mieszanie zapisów w DOM z odczytami układu powoduje problemy z układem; lepiej wykonywać odczyty przed zapisami.
Przeglądarka zawsze wykona rzeczywistą pracę, aby przekształcić kod w piksele. Celem nie jest wyeliminowanie tej pracy, lecz unikanie żądania od niej więcej, niż strona faktycznie potrzebuje.
Pozycje pokrewne
- Poza rozmiarem pakietu: odkrywanie tego, co naprawdę spowalnia twoją aplikację webową — Dlaczego redukcja kilobajtów rzadko naprawia wolną aplikację oraz jak śledzić rzeczywisty czas oczekiwania na serwerach, w procesach typu waterfall, podczas hydratacji strony, a także w skryptach i obrazach od dostawców zewnętrznych.
- Natywne API przeglądarek zastępujące popularne pakiety npm w 2026 — Wyjaśnia, w jaki sposób natywne funkcje JavaScript i CSS, takie jak Signals, operator pipeline, Temporal oraz Anchor Positioning, zastępują powszechnie używane pakiety npm.
- Skąd pochodzą wartości CSS gdy ustawiasz none: jak działa dziedziczenie — Dowiedz się, jak przeglądarki decydują, czy dana właściwość będzie dziedziczona, dlaczego elementy potomne otrzymują obliczoną wartość elementu nadrzędnego oraz w jaki sposób funkcje inherit i initial umożliwiają precyzyjną kontrolę.
- Od 66% do 185px: Jak przeglądarki rozwiązują wartości CSS przed ułożeniem — Prześledź wartość CSS na wszystkich etapach: deklarowanej, kaskadowej, określonej, obliczonej, wykorzystanej i faktycznej, oraz dowiedz się, dlaczego jednostki względne i trik konwersji rem zachowują się w taki sposób.
- Struktura, styl, behawior: Jak HTML, CSS i JavaScript dzielą pracę — Przystępna dla początkujących mapa pokazująca, co każdy z tych elementów odpowiada za stronę internetową, jak ze sobą współpracują oraz jakie powszechne błędy zamazują granice między nimi.
- Think in Invalidation: Praktyczny model tego, jak przeglądarki renderują strony — Naucz się rozumieć proces renderowania w przeglądarkach, zadając pytanie o to, co każda zmiana unieważnia, począwszy od problemów z układem i kosztu renderowania obrazu, aż po kompozycję, hydratację i strumieniowanie.