L'entrée est transmise au compositeur
Il s'agit de la dernière partie de la série de blogs en quatre parties qui explore Chrome et examine comment il gère notre code pour afficher un site Web. Dans l'article précédent, nous avons examiné le processus de rendu et découvert le moteur de rendu. Dans cet article, nous allons voir comment le moteur de rendu permet une interaction fluide lorsque l'utilisateur saisit des entrées.
Événements d'entrée du point de vue du navigateur
Lorsque vous entendez "événements de saisie", vous ne pensez peut-être qu'à la saisie dans une zone de texte ou au clic de souris, mais du point de vue du navigateur, la saisie désigne tout geste de l'utilisateur. Le défilement de la molette de la souris est un événement d'entrée, et le toucher ou le survol de la souris est également un événement d'entrée.
Lorsque l'utilisateur effectue un geste, comme un toucher sur un écran, c'est le processus du navigateur qui reçoit le geste en premier lieu. Toutefois, le processus du navigateur ne sait que où ce geste s'est produit, car le contenu d'un onglet est géré par le processus de rendu. Le processus du navigateur envoie donc le type d'événement (par exemple, touchstart
) et ses coordonnées au processus du moteur de rendu. Le processus du moteur de rendu gère l'événement de manière appropriée en recherchant la cible de l'événement et en exécutant les écouteurs d'événements associés.
Le compositeur reçoit des événements d'entrée
Dans l'article précédent, nous avons vu comment le compositeur pouvait gérer le défilement de manière fluide en composant des couches rastérisées. Si aucun écouteur d'événement d'entrée n'est associé à la page, le thread du moteur de composition peut créer un nouveau frame composite complètement indépendant du thread principal. Mais que se passerait-il si des écouteurs d'événements étaient associés à la page ? Comment le thread du compositeur peut-il savoir si l'événement doit être géré ?
Comprendre la région à défilement non rapide
Étant donné que l'exécution de JavaScript est la tâche du thread principal, lorsqu'une page est composée, le thread du compilateur marque une région de la page à laquelle des gestionnaires d'événements sont associés en tant que "Région non à défilement rapide". Grâce à ces informations, le thread du compositeur peut s'assurer d'envoyer l'événement d'entrée au thread principal si l'événement se produit dans cette région. Si l'événement d'entrée provient de l'extérieur de cette région, le thread du compositeur continue de composer un nouveau frame sans attendre le thread principal.
Faites attention lorsque vous écrivez des gestionnaires d'événements
La délégation d'événements est un modèle de gestion des événements courant en développement Web. Étant donné que les événements se propagent, vous pouvez associer un gestionnaire d'événements à l'élément le plus élevé et déléguer des tâches en fonction de la cible de l'événement. Vous avez peut-être vu ou écrit du code semblable à celui ci-dessous.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Étant donné que vous n'avez besoin d'écrire qu'un seul gestionnaire d'événements pour tous les éléments, l'ergonomie de ce modèle de délégation d'événements est attrayante. Toutefois, si vous examinez ce code du point de vue du navigateur, la page entière est désormais marquée comme une région non déroulable. Cela signifie que même si votre application ne se soucie pas de l'entrée provenant de certaines parties de la page, le thread du compositeur doit communiquer avec le thread principal et l'attendre chaque fois qu'un événement d'entrée est reçu. Par conséquent, la fonctionnalité de défilement fluide du compositeur est annulée.
Pour éviter cela, vous pouvez transmettre des options passive: true
dans votre écouteur d'événements. Cela indique au navigateur que vous souhaitez toujours écouter l'événement dans le thread principal, mais que le compositeur peut également composer un nouveau frame.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Vérifier si l'événement peut être annulé
Imaginons que vous souhaitiez limiter la direction de défilement d'une zone d'une page au défilement horizontal uniquement.
L'utilisation de l'option passive: true
dans votre événement de pointeur signifie que le défilement de la page peut être fluide, mais que le défilement vertical a peut-être commencé au moment où vous souhaitez utiliser preventDefault
afin de limiter la direction de défilement. Pour ce faire, utilisez la méthode 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});
Vous pouvez également utiliser une règle CSS telle que touch-action
pour éliminer complètement le gestionnaire d'événements.
#area {
touch-action: pan-x;
}
Trouver la cible de l'événement
Lorsque le thread du moteur de rendu envoie un événement d'entrée au thread principal, un test de contact est exécuté en premier pour trouver la cible de l'événement. Le test de collision utilise les données des enregistrements de peinture générés lors du processus de rendu pour déterminer ce qui se trouve sous les coordonnées du point où l'événement s'est produit.
Réduire les distributions d'événements au thread principal
Dans l'article précédent, nous avons vu que l'écran standard actualise l'écran 60 fois par seconde et comment suivre la cadence pour une animation fluide. Pour l'entrée, un appareil à écran tactile standard génère des événements tactiles 60 à 120 fois par seconde, et une souris standard génère des événements 100 fois par seconde. L'événement d'entrée est plus fidèle que ce que notre écran peut actualiser.
Si un événement continu tel que touchmove
a été envoyé au thread principal 120 fois par seconde, il peut déclencher un nombre excessif de tests de positionnement et d'exécution de JavaScript par rapport à la lenteur d'actualisation de l'écran.
Pour réduire les appels excessifs au thread principal, Chrome fusionne les événements continus (tels que wheel
, mousewheel
, mousemove
, pointermove
et touchmove
) et retarde l'envoi jusqu'à la veille du prochain requestAnimationFrame
.
Tous les événements distincts tels que keydown
, keyup
, mouseup
, mousedown
, touchstart
et touchend
sont envoyés immédiatement.
Utiliser getCoalescedEvents
pour obtenir des événements intra-frame
Pour la plupart des applications Web, les événements fusionnés devraient suffire à offrir une bonne expérience utilisateur.
Toutefois, si vous créez une application de dessin et que vous placez un tracé en fonction de coordonnées touchmove
, vous risquez de perdre des coordonnées entre les coordonnées pour tracer une ligne lisse. Dans ce cas, vous pouvez utiliser la méthode getCoalescedEvents
dans l'événement du pointeur pour obtenir des informations sur ces événements fusionnés.
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.
}
});
Étapes suivantes
Dans cette série, nous avons abordé le fonctionnement
interne d'un navigateur Web. Si vous n'avez jamais réfléchi aux raisons pour lesquelles DevTools recommande d'ajouter {passive: true}
à votre gestionnaire d'événements ou pourquoi vous pourriez écrire un attribut async
dans votre tag de script, j'espère que cette série vous aidera à comprendre pourquoi un navigateur a besoin de ces informations pour offrir une expérience Web plus rapide et plus fluide.
Utiliser Lighthouse
Si vous souhaitez que votre code soit agréable pour le navigateur, mais que vous ne savez pas par où commencer, Lighthouse est un outil qui exécute un audit de n'importe quel site Web et vous fournit un rapport sur ce qui fonctionne bien et ce qui doit être amélioré. La lecture de la liste des audits vous donne également une idée des éléments qui intéressent un navigateur.
Découvrez comment mesurer les performances.
Les ajustements des performances peuvent varier d'un site à l'autre. Il est donc essentiel de mesurer les performances de votre site et de déterminer ce qui lui convient le mieux. L'équipe des outils pour les développeurs Chrome propose quelques tutoriels sur la mesure des performances de votre site.
Ajouter le règlement sur les fonctionnalités à votre site
Si vous souhaitez aller plus loin, la politique relative aux fonctionnalités est une nouvelle fonctionnalité de la plate-forme Web qui peut vous servir de garde-fou lorsque vous créez votre projet. Activer le règlement sur les fonctionnalités garantit le comportement de votre application et vous évite de commettre des erreurs.
Par exemple, si vous souhaitez vous assurer que votre application ne bloquera jamais l'analyse, vous pouvez l'exécuter sur une stratégie de scripts synchrones. Lorsque sync-script: 'none'
est activé, l'exécution du code JavaScript bloquant l'analyseur est bloquée. Cela empêche votre code de bloquer l'analyseur, et le navigateur n'a pas besoin de s'inquiéter de la mise en pause de l'analyseur.
Conclusion
Lorsque j'ai commencé à créer des sites Web, je ne me souciais presque que de la façon dont j'écrivais mon code et de ce qui m'aiderait à être plus productif. Ces éléments sont importants, mais nous devons également réfléchir à la façon dont le navigateur récupère le code que nous écrivons. Les navigateurs modernes ont investi et continuent d'investir dans des moyens de fournir une meilleure expérience Web aux utilisateurs. Être gentil avec le navigateur en organisant notre code, à son tour, améliore votre expérience utilisateur. J'espère que vous me rejoindrez dans cette quête de gentillesse envers les navigateurs.
Merci beaucoup à tous ceux qui ont examiné les premières versions de cette série, y compris (mais sans s'y limiter): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda et Charlie Yasuda.{/13
Avez-vous apprécié cette série ? Si vous avez des questions ou des suggestions pour un prochain post, n'hésitez pas à me contacter dans la section des commentaires ci-dessous ou sur Twitter en suivant @kosamari.