Nowoczesna przeglądarka internetowa (część 4)

Mariko Kosaka

Dane wejściowe wkrótce pojawią się w usłudze Compositor

To jest ostatni z czterech postów na blogu o tym, jak wygląda Chrome. Opisujemy w nim, jak obsługuje on nasz kod do wyświetlania witryny. W poprzednim poście omówiliśmy proces renderowania i kompozytor. W tym poście dowiemy się, jak kompozytor umożliwia płynną interakcję po wprowadzeniu zmian przez użytkownika.

Zdarzenia wejściowe z perspektywy przeglądarki

Gdy słyszysz „zdarzenia wejściowe”, przychodzą Ci tylko na myśl pisanie w polu tekstowym lub kliknięciu myszą. Z perspektywy przeglądarki wynika to, że wprowadzanie danych oznacza dowolny gest użytkownika. Przewijanie kółkiem myszy to zdarzenie wejściowe, a dotknięcie lub najechanie kursorem również jest zdarzeniem wejściowym.

Gdy użytkownik wykonuje gest, np. dotyka ekranu, najpierw proces przeglądarki otrzymuje ten gest. Jednak proces przeglądarki wie tylko, gdzie wystąpił ten gest, ponieważ zawartość karty jest obsługiwana przez mechanizm renderowania. Dlatego proces przeglądarki wysyła do mechanizmu renderowania typ zdarzenia (np. touchstart) i jego współrzędne. Proces renderowania odpowiednio obsługuje zdarzenie, znajdując jego miejsce docelowe i uruchamiając dołączone detektory zdarzeń.

zdarzenie wejściowe
Rysunek 1. Zdarzenie wejściowe kierowane przez proces przeglądarki do procesu renderowania

Kompozytor odbiera zdarzenia wejściowe

Rysunek 2. Widoczny obszar najeżdżający na warstwy strony

W poprzednim poście omówiliśmy, jak kompozytor może płynnie obsługiwać przewijanie dzięki komponowaniu warstw zrastrowanych. Jeśli do strony nie są dołączone żadne detektory zdarzeń wejściowych, wątek kompozytora może utworzyć nową ramkę złożoną całkowicie niezależną od wątku głównego. Ale co, jeśli do strony dołączono niektóre detektory zdarzeń? W jaki sposób wątek kompozytora sprawdzi, czy zdarzenie wymaga obsługi?

Informacje o regionie, który nie wymaga szybkiego przewijania

Uruchomienie JavaScriptu jest zadaniem głównego wątku, więc po skomponowaniu strony wątek kompozytora oznacza region strony, w którym moduły obsługi zdarzeń są dołączone jako „Region poza szybkim przewijaniem”. Dzięki tym informacjom wątek kompozytora może wysyłać zdarzenie wejściowe do wątku głównego, jeśli zdarzenie ma miejsce w danym regionie. Jeśli zdarzenie wejściowe pochodzi spoza tego regionu, wątek kompozytora kontynuuje komponowanie nowej ramki bez oczekiwania na wątek główny.

ograniczony, nieszybki obszar przewijania
Rysunek 3. Schemat opisanych danych wejściowych dla nieszybkiego obszaru przewijania

Uważaj podczas tworzenia modułów obsługi zdarzeń

Typowym wzorcem obsługi zdarzeń w przypadku tworzenia stron internetowych jest delegowanie zdarzeń. Jako dymek zdarzeń możesz dołączyć do elementu najwyższego poziomu 1 moduł obsługi zdarzeń i przekazać zadania na podstawie celu zdarzenia. Być może zdarzyło Ci się widzieć lub napisać kod taki jak poniżej.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

Musisz napisać tylko jeden moduł obsługi zdarzeń dla wszystkich elementów, więc ergonomia tego wzorca przekazywania zdarzeń jest atrakcyjna. Jeśli jednak spojrzysz na ten kod z perspektywy przeglądarki, cała strona jest oznaczona jako obszar, który nie pozwala na szybkie przewijanie. Oznacza to, że nawet jeśli aplikacja nie interesuje się danymi z pewnych części strony, wątek kompozytora musi komunikować się z wątkiem głównym i czekać na niego za każdym razem, gdy pojawi się zdarzenie wejściowe. W związku z tym kompozytor nie umożliwia płynnego przewijania.

region z możliwością przewijania bez możliwości szybkiego przewijania
Rysunek 4. Schemat opisanych danych wejściowych dotyczących nieszybkiego przewijania w obszarze obejmującym całą stronę

Aby temu zapobiec, możesz przekazać w odbiorniku zdarzenia opcje passive: true. Wskazuje to przeglądarce, że nadal chcesz nasłuchiwać zdarzenia w wątku głównym, ale kompozytor może też skomponować nową ramkę.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

Sprawdzanie, czy wydarzenie można anulować

przewijanie strony
Rysunek 5. Strona internetowa z częścią strony umieszczoną w pionie z przewijaniem poziomym

Wyobraź sobie, że masz na stronie pole, które chcesz ograniczyć kierunek przewijania tylko do przewijania w poziomie.

Używanie opcji passive: true w zdarzeniu wskaźnika oznacza, że przewijanie strony może być płynne, ale przewinięcie w pionie mogło już zacząć się w wybranym przez Ciebie momencie preventDefault w celu ograniczenia kierunku przewijania. Możesz to sprawdzić za pomocą metody event.cancelable.

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // block the native scroll
        /*
        *  do what you want the application to do here
        */
    }
}, {passive: true});

Możesz też użyć reguły CSS takiej jak touch-action, aby całkowicie wyeliminować moduł obsługi zdarzeń.

#area {
  touch-action: pan-x;
}

Znajdowanie celu zdarzenia

test pozycji
Rysunek 6. Główny wątek przyglądający się rekordom farby pytający, co narysowano w punkcie x.y

Gdy wątek kompozytora wysyła zdarzenie wejściowe do wątku głównego, najpierw należy przeprowadzić test trafień, aby znaleźć miejsce docelowe zdarzenia. Test działania wykorzystuje dane renderowania wygenerowane w trakcie procesu renderowania, aby ustalić, co znajduje się pod współrzędnymi punktu, w którym wystąpiło zdarzenie.

Minimalizowanie wysyłanych zdarzeń do wątku głównego

W poprzednim poście omówiliśmy, jak nasz typowy wyświetlacz odświeża się 60 razy na sekundę i jak zadbać o płynność animacji. Jeśli chodzi o dane wejściowe, typowe urządzenie z ekranem dotykowym wywołuje zdarzenie dotyku z szybkością 60–120 razy na sekundę, a typowa mysz generuje zdarzenia 100 razy na sekundę. Zdarzenie wejściowe ma większą dokładność niż odświeżanie ekranu.

Jeśli ciągłe zdarzenie takie jak touchmove było wysyłane do wątku głównego 120 razy na sekundę, może ono wywołać nadmierną liczbę testów działań i wykonania JavaScriptu w porównaniu z powolnym odświeżaniem ekranu.

niefiltrowane zdarzenia
Rysunek 7. Zdarzenia zależne od osi czasu ramki, które powodują zacinanie się strony

Aby zminimalizować nadmierne wywołania głównego wątku, Chrome łączy ciągłe zdarzenia (takie jak wheel, mousewheel, mousemove, pointermove, touchmove) i opóźnia ich wysyłanie aż do następnego elementu (requestAnimationFrame).

połączone wydarzenia
Rysunek 8. Ten sam ramy czasowe co wcześniej, ale zdarzenie jest scalane i opóźnione

Wszystkie osobne zdarzenia, takie jak keydown, keyup, mouseup, mousedown, touchstart i touchend, są wysyłane natychmiast.

Aby otrzymywać zdarzenia w ramce, użyj polecenia getCoalescedEvents

W przypadku większości aplikacji internetowych skorelowane zdarzenia powinny wystarczyć, aby zapewnić użytkownikom dobre wrażenia. Gdy jednak tworzysz rysowanie aplikacji i podasz ścieżkę na podstawie współrzędnych touchmove, możesz utracić współrzędne podczas rysowania gładkiej linii. W takim przypadku możesz użyć w zdarzeniu wskaźnika metody getCoalescedEvents, aby uzyskać informacje o tych połączonych zdarzeniach.

getCoalescedEvents
Rysunek 9. Ścieżka gestu płynnego dotyku po lewej stronie; ograniczona ścieżka po prawej
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.
    }
});

Dalsze kroki

W tej serii pokazaliśmy, jak działa przeglądarka internetowa. Jeśli zastanawiasz się, dlaczego Narzędzia deweloperskie zalecają dodanie atrybutu {passive: true} do modułu obsługi zdarzeń lub opisanie atrybutu async w tagu skryptu, mam nadzieję, że ta seria wyjaśnia, dlaczego przeglądarka potrzebuje tych informacji do szybszego i bezproblemowego korzystania z internetu.

Użyj narzędzia Lighthouse

Jeśli chcesz, aby kod był przyjazny dla przeglądarki, ale nie wiesz, od czego zacząć, Lighthouse to narzędzie, które przeprowadza audyt dowolnej witryny i wyświetla raporty o tym, co zostało zrobione dobrze, a co wymaga ulepszenia. Czytanie listy audytów pozwala też zorientować się, jakie kwestie są istotne w przypadku danej przeglądarki.

Dowiedz się, jak mierzyć skuteczność

Zmiany w wydajności mogą się różnić w zależności od witryny, dlatego ważne jest, aby zmierzyć jej wydajność i zdecydować, co będzie dla niej najlepsze. Zespół Narzędzi deweloperskich w Chrome udostępnia kilka samouczków na temat mierzenia wydajności witryny.

Dodawanie do witryny zasad dotyczących funkcji

Jeśli chcesz zrobić coś więcej, zasady dotyczące funkcji to nowa funkcja platformy internetowej, która może stanowić barierę podczas tworzenia projektu. Włączenie zasad dotyczących funkcji gwarantuje określone działanie aplikacji i zapobiega pomyłkom. Jeśli na przykład chcesz mieć pewność, że Twoja aplikacja nigdy nie będzie blokować analizy, możesz uruchomić ją przy użyciu zasady skryptów zsynchronizowanych. Gdy zasada sync-script: 'none' jest włączona, wykonywanie kodu JavaScript blokującego parser nie jest możliwe. Dzięki temu żaden fragment kodu nie będzie blokował parsera, a przeglądarka nie będzie musiała się martwić jego wstrzymaniem.

Podsumowanie

dziękuję

Gdy zacząłem tworzyć witryny, zależało mi prawie tylko na tym, jak piszę kod i co pomogłoby mi zwiększyć wydajność. To ważne, ale powinniśmy również zastanowić się, jak przeglądarka odbiera tworzony przez nas kod. Nowoczesne przeglądarki są i stale inwestują w rozwiązania pozwalające zwiększyć wygodę użytkowników. Jeśli uporządkujesz kod, uporządkując go, poprawisz wygodę użytkowników. Mam nadzieję, że razem ze mną weźmiesz udział w wyprawie miłej dla przeglądarek!

Bardzo dziękujemy wszystkim osobom, które przeanalizowały wczesne wersje tej serii, w tym: Alex Russell, Paul Ireland, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasusko.

Czy podobała Ci się ta seria? Jeśli masz pytania lub sugestie dotyczące przyszłego posta, podziel się nimi z nami w sekcji komentarzy poniżej lub napisz @kosamari na Twitterze.