Renderowanie w przeglądarce: od tekstu do obrazów

Rysowanie ekranu to nie magia

Kiedy zaczynałem programować front-end (Vue.js), myślałem o przeglądarce tylko jako o „ramce do wyświetlania kodu”. To było naturalne, że jeśli użyjesz

, pojawi się kwadrat, a jeśli użyjesz color: red, stanie się on czerwony.

Moje zdanie uległo jednak zmianie, gdy w praktyce doświadczyłem zacinania się animacji lub szarpania ekranu podczas przewijania (jank). „Dlaczego ten sam kod działa płynnie w przeglądarce Chrome, ale ulega awarii w przeglądarce Internet Explorer (IE)?” „Dlaczego mój procesor gwałtownie rośnie, gdy zmieniam trochę styl za pomocą JavaScript?”

Okazuje się, że przeglądarka to nie tylko ramka na zdjęcia. Przeglądarka była ogromnym „malarzem” i „architektem”, który interpretował wysłany przez nas tekst (HTML/CSS), matematycznie obliczał każdy piksel i drukował go.

Ostatnim razem bezpiecznie dostarczyliśmy dane po wyboistych drogach sieciowych. Tym razem otwórzmy otrzymaną skrzynkę danych (pakiet) i dowiedzmy się, jak przeglądarka rysuje obraz zwany ekranem. To ostatnia historia z serii Re: Podstawy uruchamiania CS.

Przeglądarka to plac budowy, na którym czyta się, buduje i maluje kod.

Ładunek przybył, rozpoczyna się montaż

W naszym światopoglądzie „cyfrowego centrum logistycznego” produkt (plik HTML) dotarł już do domu klienta (przeglądarki). Jednak to, co dotarło, nie było gotowym produktem, ale „instrukcją montażu”. Zespół roboczy zwany silnikiem przeglądarki (silnikiem renderującym) czyta niniejszą instrukcję i natychmiast przystępuje do montażu. Ten proces nazywa się potokiem renderowania.

Krok 1: Analiza planu (parsowanie i drzewo DOM)

Odczytuje tekst HTML, który pojawia się jako pierwszy i tworzy „szkielet”.

  • <html> to korzeń (Root), <body> to pień, <div> to gałęzie…
  • Taką strukturę drzewa (Tree) nazywamy DOM (Document Object Model). To także API, dzięki któremu JavaScript może później sięgać do tego szkieletu i go zmieniać.

Krok 2: Projektowanie wnętrz (drzewo CSSOM)

Następnie przeczytaj plik CSS i utwórz „reguły stylu”.

  • „Wszystkie div są czerwone”, „element z klasą box ma rozmiar 100px”
  • Nazywa się to CSSOM (CSS Object Model).

Krok 3: Utwórz zlecenie pracy (drzewo renderowania)

Teraz łączymy szkielet (DOM) i projekt (CSSOM). Ważne jest to, że wybierane są tylko „rzeczy, które faktycznie pojawią się na ekranie”.

  • A element z właściwością display: none? Nie będzie widoczny na ekranie, więc zostaje wykluczony z drzewa renderowania. (Nie zajmuje też miejsca)
  • Natomiast visibility: hidden? Element jest niewidoczny, ale nadal zajmuje miejsce, więc zostaje uwzględniony w drzewie renderowania.
Drzewo renderowania to ostateczna lista tylko „tych, które można narysować naprawdę”.

[Weryfikacja kodu] Reflow i repaint

Teraz nadszedł naprawdę ważny krok. Tutaj leży klucz do optymalizacji wydajności. Po utworzeniu drzewa renderowania przeglądarka wykonuje dwa ciężkie zadania.

  1. Układ (Layout) / Reflow: matematycznie oblicza, „gdzie i w jakim rozmiarze” zostanie umieszczony każdy element. (Projekt architektoniczny)
  2. Malowanie (Paint) / Repaint: w obliczonych miejscach faktycznie „nakłada kolory”. (Prace malarskie)

Poczujmy różnicę w kosztach dzięki kodowi.

const box = document.getElementById('box');

// Case 1: Kod wywolujacy reflow (drogi!)
// Przy zmianie rozmiaru lub pozycji, sasiadujace elementy musza zostac przeliczone.
box.style.width = '200px'; 
box.style.height = '200px';
box.style.margin = '20px';

// Case 2: Kod wywolujacy tylko repaint (stosunkowo tani)
// Pozycja bez zmian, zmienia sie tylko kolor. Bez przeliczania, tylko ponowne malowanie.
box.style.backgroundColor = 'blue';
box.style.color = 'white';
box.style.visibility = 'hidden';

Analiza:

  • Zmiana width: to przebudowa, która zmienia rozmiar budynku. Sąsiedni budynek zostaje odsunięty, a za nim kolejny. Przeglądarka musi wszystko przeliczyć od nowa. (Zachodzi Reflow -> główny winowajca spadku wydajności)
  • Zmiana color: rozmiar budynku zostaje bez zmian, odświeża się tylko farbę. Nie trzeba niczego przeliczać. (Zachodzi Repaint -> szybko)

W praktyce większość przypadków „zacinającej się animacji” ma miejsce, gdy właściwości układu, takie jak width, left i top są zmieniane w czasie rzeczywistym. W takim przypadku potrzebna jest technika pominięcia obliczeń układu za pomocą właściwości takich jak transform.

Praktyczna rada: nie znęcaj się nad swoją przeglądarką

Nie wiedziałem o tym, kiedy byłem na studiach, więc losowo manipulowałem DOMem za pomocą JavaScript. Przeglądając instrukcję for, zmieniłem rozmiar i styl każdego elementu. Z perspektywy przeglądarki przypomina to torturowanie niszczenia i odbudowy budynku dziesiątki razy na sekundę.

Wskazówki dotyczące zapewnienia wydajności:

  1. Zmiany w DOM grupuj i wykonuj za jednym razem: nie zmieniaj stylów pojedynczo, tylko przygotuj wcześniej class i jednym ruchem podmień className.
  2. Animacje zostaw GPU: jeśli zamiast left i top użyjesz transform: translate(), etap układu zostanie pominięty, a pracę przejmie GPU.
  3. Różnica przy ukrywaniu: świadomie wybieraj między display: none (wykluczony z drzewa renderowania), gdy element ma całkiem zniknąć, a visibility: hidden (uwzględniony w drzewie renderowania), gdy ma po nim zostać tylko puste miejsce.
Zmiana lokalizacji (Reflow) jest operacją znacznie droższą niż zmiana koloru (Repaint).

Zakończenie serii 1: Mój CS został ponownie uruchomiony

Tym artykułem zakończyła się seria [Re: Booting] CS Basics.

Odbyliśmy razem długą podróż.

  1. Język (Language): kod napisany przez człowieka został przetłumaczony na język maszynowy (build),
  2. Pamięć (Memory): zamieszkał w pokojach zwanych stosem i stertą,
  3. Proces (OS): dzięki klonom zwanym wątkami dzielił się procesorem,
  4. Sieć (Network): rozbity na pakiety, przemknął po wyboistych drogach,
  5. Renderowanie (Rendering): aż w końcu spotkał malarza zwanego przeglądarką i pojawił się przed naszymi oczami.

Na początku wystarczyła mi wiedza, którą zapamiętałem, aby dostać pracę. Jednak po napotkaniu błędów w praktyce i ponownym przyjrzeniu się, CS (inżynieria komputerowa) nie okazała się martwą literą, ale solidnym gruntem, który wspierał mój kod.

Prace fundamentowe są już ukończone. Nadszedł czas, aby zbudować własny zamek na solidnym gruncie. W kolejnej serii [Monolith Builder] omówię budowanie aplikacji internetowych przy użyciu Spring Boot i Vue.js, czego doświadczyłem w praktyce. Z niecierpliwością czekamy na proces przekształcania „teorii” w „praktykę”.

Dodaj komentarz