Dane wejściowe docierają do kompozytora
To ostatni z 4 artykułów z cyklu, w którym przyjrzeliśmy się Chrome od środka, aby pokazać, jak przeglądarka obsługuje nasz kod podczas wyświetlania strony internetowej. W poprzednim poście przyglądaliśmy się procesowi renderowania i znajdowaliśmy kompozytor. W tym poście omówimy, jak kompozytor umożliwia płynną interakcję po wprowadzeniu danych przez użytkownika.
Zdarzenia wejściowe z perspektywy przeglądarki
Gdy słyszysz „zdarzenia wejściowe”, możesz myśleć tylko o wpisywaniu tekstu w polu tekstowym lub kliknięciu myszy, ale z perspektywy przeglądarki wprowadzanie tekstu oznacza dowolny gest użytkownika. Przewijanie kółkiem myszy to zdarzenie wprowadzania danych, a dotknięcie lub najechanie kursorem na element to też zdarzenie wprowadzania danych.
Gdy użytkownik wykona gest, np. dotknie ekranu, proces przeglądarki jest pierwszym, który odbiera gest. Proces przeglądarki wie jednak tylko, gdzie wystąpił ten gest, ponieważ treści na karcie są obsługiwane przez proces renderowania. Proces przeglądarki wysyła typ zdarzenia (np. touchstart
) i jego współrzędne do procesu renderowania. Proces renderowania odpowiednio obsługuje zdarzenia, znajdując miejsce docelowe zdarzenia i uruchamiając dołączone detektory zdarzeń.
Kompozytor odbiera zdarzenia wejściowe
W poprzednim poście omawialiśmy, jak kompozytor może obsługiwać płynne przewijanie, łącząc zeskalowane warstwy. Jeśli do strony nie jest dołączony żaden odbiorca zdarzenia wejściowego, wątek kompozytora może utworzyć nową ramkę złożoną całkowicie niezależnie od wątku głównego. Co jednak, jeśli do strony zostały dołączone niektóre odbiorniki zdarzeń? Jak wątek kompozytora ma się dowiedzieć, czy zdarzenie wymaga obsługi?
Region z wolnym przewijaniem
Uruchamianie kodu JavaScript to zadanie głównego wątku, więc gdy strona jest złożona, wątek kompozytora oznacza region strony z dołączonymi obslugami zdarzeń jako „Obszar nieprzewijalny szybko”. Dzięki tym informacjom wątek kompozytora może wysłać zdarzenie wejściowe do głównego wątku, jeśli zdarzenie wystąpi w tym regionie. Jeśli zdarzenie wejściowe pochodzi spoza tego regionu, wątek kompozytora kontynuuje komponowanie nowej ramki, nie czekając na wątek główny.
Podczas pisania modułów obsługi zdarzeń pamiętaj o następujących kwestiach:
Typowym wzorcem obsługi zdarzeń w programowaniu stron internetowych jest delegowanie zdarzeń. Zdarzenia są przekazywane dalej, więc możesz dołączyć jeden moduł obsługi zdarzeń do najwyższego elementu i przekazać zadania na podstawie celu zdarzenia. Możesz widzieć lub pisać kod podobny do tego:
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Dla wszystkich elementów wystarczy utworzyć jeden moduł obsługi zdarzeń, więc ergonomia tego wzorca przekazywania zdarzeń jest atrakcyjna. Jeśli jednak spojrzysz na ten kod z perspektywy przeglądarki, zobaczysz, że cała strona jest oznaczona jako obszar, po którym nie można szybko przewijać. Oznacza to, że nawet jeśli Twoja aplikacja nie potrzebuje danych wejściowych z pewnych części strony, wątek kompozytora musi się komunikować z głównym wątkiem i czekać na niego za każdym razem, gdy pojawi się zdarzenie wejściowe. W ten sposób kompozytor nie będzie mógł płynnie przewijać.
Aby temu zapobiec, możesz przekazać opcje passive: true
do swojego odbiornika zdarzeń. To sugeruje przeglądarce, że nadal chcesz słuchać zdarzenia w głównym wątku, ale kompozytor może też skompilować nowy obraz.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Sprawdź, czy można anulować zdarzenie
Wyobraź sobie, że na stronie masz pole, w którym chcesz ograniczyć kierunek przewijania tylko do poziomego.
Użycie opcji passive: true
w zdarzeniu wskaźnika spowoduje, że przewijanie strony może być płynne, ale pionowe przewijanie może się rozpocząć w chwili, gdy chcesz użyć opcji preventDefault
, aby ograniczyć kierunek przewijania. Możesz to sprawdzić, używając 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, np. touch-action
, aby całkowicie wyeliminować obciążenie.
#area {
touch-action: pan-x;
}
Znajdowanie celu zdarzenia
Gdy wątek kompozytora wysyła zdarzenie wejścia do głównego wątku, najpierw wykonywany jest test uderzenia, aby znaleźć cel zdarzenia. Test uderzenia korzysta z danych rekordów malowania wygenerowanych w procesie renderowania, aby ustalić, co znajduje się pod współrzędnymi punktu, w którym wystąpiło zdarzenie.
Minimalizowanie wysyłania zdarzeń do wątku głównego
W poprzednim poście omawialiśmy, jak typowy wyświetlacz odświeża ekran 60 razy na sekundę i jak musimy dostosować się do tempa, aby zapewnić płynną animację. W przypadku urządzeń z ekranem dotykowym zdarzenie dotyku występuje 60–120 razy na sekundę, a w przypadku myszy – 100 razy na sekundę. Zdarzenie wejściowe ma wyższą dokładność, niż można odświeżyć ekran.
Jeśli ciągłe zdarzenie, np. touchmove
, jest wysyłane do głównego wątku 120 razy na sekundę, może to spowodować nadmierną liczbę testów trafień i wykonania kodu JavaScript w porównaniu z wolnym odświeżaniem ekranu.
Aby zminimalizować liczbę wywołań wątku głównego, Chrome łączy zdarzenia ciągłe (np. wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) i opóźnia wysyłanie wiadomości aż do momentu przed następnym requestAnimationFrame
.
Wszystkie konkretne zdarzenia, takie jak keydown
, keyup
, mouseup
, mousedown
, touchstart
i touchend
, są wysyłane natychmiast.
Użyj getCoalescedEvents
, aby uzyskać zdarzenia wewnątrz ramki
W przypadku większości aplikacji internetowych wystarczy użycie złączonych zdarzeń, aby zapewnić użytkownikom wygodę.
Jeśli jednak tworzysz takie elementy, jak aplikacja do rysowania i umieszczasz ścieżkę na podstawie współrzędnych touchmove
, możesz utracić różnice między współrzędnymi w celu narysowania gładkiej linii. W takim przypadku możesz użyć metody getCoalescedEvents
w zdarzeniu wskazywania, aby uzyskać informacje o tych scalonych zdarzeniach.
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 omawialiśmy działanie przeglądarki internetowej. Jeśli nigdy nie zastanawiałeś(-aś) się, dlaczego DevTools zaleca dodanie atrybutu {passive: true}
do Twojego przetwarzacza zdarzeń lub dlaczego warto dodać atrybut async
do tagu skryptu, mam nadzieję, że ta seria artykułów pomoże Ci zrozumieć, dlaczego przeglądarka potrzebuje tych informacji, aby zapewnić szybsze i płynniejsze działanie internetu.
Korzystanie z 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 każdej witryny i wyświetla raport pokazujący, co zostało zrobione prawidłowo, a co wymaga ulepszenia. Jeśli czytasz listę audytów, zorientujesz się, na co zwraca uwagę przeglądarka.
Dowiedz się, jak mierzyć skuteczność
W zależności od witryny zmiany w jej wydajności mogą się różnić, dlatego ważne jest, aby mierzyć jej wydajność i decydowować, co jest dla niej najlepsze. Zespół Narzędzi deweloperskich w Chrome udostępnia kilka samouczków na temat mierzenia wydajności witryny.
Dodawanie polityki dotyczącej funkcji do witryny
Jeśli chcesz podjąć dodatkowy krok, Zasady dotyczące funkcji to nowa funkcja platformy internetowej, która może być dla Ciebie zabezpieczeniem podczas tworzenia projektu. Włączenie zasad dotyczących funkcji gwarantuje określone działanie aplikacji i zapobiega popełnianiu błędów.
Jeśli na przykład chcesz mieć pewność, że aplikacja nigdy nie będzie blokować analizy, możesz uruchomić w niej zasadę skryptów synchronicznych. Gdy funkcja sync-script: 'none'
jest włączona, nie można wykonać kodu JavaScript blokującego parser. Dzięki temu żaden kod nie będzie blokował parsowania, a przeglądarka nie musi się martwić o wstrzymywanie parsowania.
Podsumowanie
Gdy zaczynałam tworzyć strony internetowe, zależało mi tylko na tym, jak pisać kod i co może zwiększyć moją produktywność. Te kwestie są ważne, ale powinniśmy też zastanowić się, jak przeglądarka interpretuje nasz kod. Współczesne przeglądarki stale inwestują w rozwiązania, które mają zapewnić użytkownikom lepsze wrażenia z korzystania z Internetu. Uporządkowanie kodu w celu ułatwienia przeglądarce jego odczytania poprawi wrażenia użytkownika. Mam nadzieję, że dołączysz do mnie w questu polegającym na łagodnym traktowaniu przeglądarek.
Dziękujemy wszystkim, którzy sprawdzili wczesne wersje tej serii, w tym (ale nie tylko): Alexowi Russellowi, Paulowi Irishowi, Meggin Kearney, Ericowi Bidelmanowi, Mathiasowi Bynensowi, Addy Osmani, Kinuko Yasudzie, Nasko Oskovowi i Charliemu Reisowi.
Podobała Ci się ta seria? Jeśli masz pytania lub sugestie dotyczące przyszłego wpisu, daj nam znać w sekcji komentarzy poniżej lub na Twitterze, pisząc do @kosamari.