Optimiser l'interaction pour la page suivante

Découvrez comment optimiser l'interaction jusqu'au prochain rendu de votre site Web.

L'Interaction to Next Paint (INP) est une métrique Core Web Vitals stable qui évalue la réactivité globale d'une page aux interactions des utilisateurs en observant la latence de toutes les interactions éligibles survenant au cours de la visite d'un utilisateur sur la page. La valeur INP finale correspond à l'interaction la plus longue observée (en ignorant parfois les anomalies).

Pour offrir une expérience utilisateur de qualité, les sites Web doivent s'efforcer de ne pas dépasser 200 millisecondes pour l'interaction jusqu'à la prochaine peinture. Pour vous assurer d'atteindre cet objectif pour la plupart de vos utilisateurs, nous vous recommandons de mesurer le 75e centile des chargements de pages, segmenté par appareil mobile et ordinateur.

Une valeur INP de 200 millisecondes ou moins est considérée comme bonne, une valeur supérieure à 500 millisecondes comme mauvaise, et une valeur comprise entre ces deux valeurs doit être améliorée.

Selon le site Web, il peut y avoir peu ou pas d'interactions (par exemple, des pages contenant principalement du texte et des images, avec peu ou pas d'éléments interactifs). Dans le cas de sites Web tels que des éditeurs de texte ou des jeux, il peut y avoir des centaines, voire des milliers d'interactions. Dans les deux cas, lorsque l'INP est élevé, l'expérience utilisateur est mise en péril.

Améliorer l'INP demande du temps et des efforts, mais vous serez récompensé par une meilleure expérience utilisateur. Ce guide vous explique comment améliorer l'INP.

Déterminer la cause d'une mauvaise qualité de l'INP

Avant de pouvoir corriger les interactions lentes, vous devez disposer de données pour savoir si l'INP de votre site Web est faible ou doit être amélioré. Une fois que vous disposez de ces informations, vous pouvez passer au laboratoire pour commencer à diagnostiquer les interactions lentes et trouver une solution.

Identifier les interactions lentes sur le terrain

Idéalement, votre parcours d'optimisation de l'INP doit commencer par les données sur le terrain. Dans le meilleur des cas, les données sur le terrain d'un fournisseur de surveillance des utilisateurs réels (RUM) vous fourniront non seulement la valeur INP d'une page, mais aussi des données contextuelles qui mettent en évidence l'interaction spécifique à l'origine de la valeur INP elle-même, si l'interaction s'est produite pendant ou après le chargement de la page, le type d'interaction (clic, pression sur une touche ou appui), ainsi que d'autres informations utiles.

Si vous ne vous appuyez pas sur un fournisseur de RUM pour obtenir des données sur le terrain, le guide des données sur le terrain de l'INP vous recommande d'utiliser le rapport d'expérience utilisateur Chrome (CrUX) via PageSpeed Insights pour combler les lacunes. CrUX est l'ensemble de données officiel du programme Core Web Vitals. Il fournit un résumé général des métriques pour des millions de sites Web, y compris l'INP. Cependant, CrUX ne fournit souvent pas les données contextuelles que vous obtiendriez d'un fournisseur de RUM pour vous aider à analyser les problèmes. C'est pourquoi nous recommandons toujours aux sites d'utiliser un fournisseur de RUM dans la mesure du possible ou d'implémenter leur propre solution de RUM pour compléter ce qui est disponible dans CrUX.

Diagnostiquer les interactions lentes dans l'atelier

Idéalement, vous devez commencer les tests en laboratoire une fois que vous disposez de données sur le terrain qui suggèrent que les interactions sont lentes. En l'absence de données sur le terrain, il existe des stratégies permettant d'identifier les interactions lentes en laboratoire. Ces stratégies incluent le suivi des parcours utilisateur courants et le test des interactions tout au long du processus, ainsi que l'interaction avec la page pendant le chargement (lorsque le thread principal est souvent le plus chargé) afin de détecter les interactions lentes pendant cette partie cruciale de l'expérience utilisateur.

Optimiser les interactions

Une fois que vous avez identifié une interaction lente et que vous pouvez la reproduire manuellement dans l'atelier, l'étape suivante consiste à l'optimiser. Les interactions peuvent être divisées en trois phases:

  1. Le délai d'entrée, qui commence lorsque l'utilisateur entame une interaction avec la page et se termine lorsque les rappels d'événement pour l'interaction commencent à s'exécuter.
  2. La durée de traitement, qui correspond au temps nécessaire pour que les rappels d'événements s'exécutent.
  3. Le délai de présentation, qui correspond au temps nécessaire au navigateur pour présenter le frame suivant contenant le résultat visuel de l'interaction.

La somme de ces trois phases correspond à la latence d'interaction totale. Chaque phase d'une interaction contribue à la latence totale de l'interaction. Il est donc important de savoir comment optimiser chaque partie de l'interaction pour qu'elle dure le moins longtemps possible.

Identifier et réduire le délai de réponse à l'entrée

Lorsqu'un utilisateur interagit avec une page, la première partie de cette interaction est le délai d'entrée. En fonction de l'activité sur la page, les délais de saisie peuvent être très longs. Cela peut être dû à une activité sur le thread principal (par exemple, en raison du chargement, de l'analyse et de la compilation de scripts), à la gestion des récupérations, aux fonctions de minuteur ou même à d'autres interactions qui se produisent rapidement et se chevauchent.

Quelle que soit la source du délai d'entrée d'une interaction, vous devez le réduire au minimum afin que les interactions puissent commencer à exécuter les rappels d'événement dès que possible.

Relation entre l'évaluation du script et les tâches longues au démarrage

L'interactivité au démarrage est un aspect essentiel du cycle de vie des pages. Lorsqu'une page se charge, elle s'affiche initialement, mais il est important de se rappeler que ce n'est pas parce qu'une page s'est affichée qu'elle a fini de se charger. En fonction du nombre de ressources dont une page a besoin pour être entièrement fonctionnelle, il est possible que les utilisateurs tentent d'interagir avec la page pendant qu'elle se charge.

L'évaluation du script peut prolonger le délai d'entrée d'une interaction pendant le chargement d'une page. Une fois qu'un fichier JavaScript a été extrait du réseau, le navigateur doit encore effectuer des tâches avant que le code JavaScript puisse s'exécuter. Ces tâches incluent l'analyse d'un script pour s'assurer que sa syntaxe est valide, sa compilation en bytecode, puis son exécution.

En fonction de la taille d'un script, ce travail peut entraîner des tâches longues sur le thread principal, ce qui retarde la réponse du navigateur aux autres interactions utilisateur. Pour que votre page reste réactive aux entrées utilisateur pendant le chargement, il est important de comprendre ce que vous pouvez faire pour réduire la probabilité de tâches longues pendant le chargement de la page afin qu'elle reste rapide.

Optimiser les rappels d'événement

Le délai d'entrée n'est que la première partie de ce que mesure l'INP. Vous devez également vous assurer que les rappels d'événements exécutés en réponse à une interaction utilisateur peuvent se terminer aussi rapidement que possible.

Cédez souvent au thread principal

Le meilleur conseil général pour optimiser les rappels d'événements est de ne pas y effectuer autant de travail que possible. Cependant, votre logique d'interaction peut être complexe, et vous ne pourrez peut-être réduire que marginalement le travail qu'elles effectuent.

Si vous constatez que c'est le cas pour votre site Web, vous pouvez essayer de diviser le travail dans les rappels d'événements en tâches distinctes. Cela évite que le travail collectif ne devienne une tâche longue qui bloque le thread principal, ce qui permet aux autres interactions qui seraient autrement en attente sur le thread principal de s'exécuter plus tôt.

setTimeout est un moyen de diviser les tâches, car le rappel qui lui est transmis s'exécute dans une nouvelle tâche. Vous pouvez utiliser setTimeout seul ou l'extraire dans une fonction distincte pour une génération plus ergonomique.

Il est préférable de céder indistinctement que de ne pas céder du tout. Toutefois, il existe une méthode plus nuancée de céder au thread principal, qui consiste à céder immédiatement après un rappel d'événement qui met à jour l'interface utilisateur afin que la logique de rendu puisse s'exécuter plus tôt.

Rendement pour permettre l'exécution du rendu plus tôt

Une technique de rendement plus avancée consiste à structurer le code dans vos rappels d'événements pour limiter l'exécution à la seule logique requise pour appliquer les mises à jour visuelles pour le prochain frame. Tout le reste peut être reporté à une tâche ultérieure. Cela permet non seulement de garder les rappels légers et agiles, mais aussi d'améliorer le temps de rendu des interactions en empêchant les mises à jour visuelles de bloquer le code de rappel d'événement.

Par exemple, imaginez un éditeur de texte enrichi qui met en forme le texte à mesure que vous le saisissez, mais qui met également à jour d'autres aspects de l'interface utilisateur en fonction de ce que vous avez écrit (nombre de mots, mise en évidence des fautes d'orthographe et autres commentaires visuels importants, par exemple). De plus, l'application peut également avoir besoin d'enregistrer ce que vous avez écrit pour que vous ne perdiez pas votre travail si vous quittez l'application et y revenez.

Dans cet exemple, les quatre éléments suivants doivent se produire en réponse aux caractères saisis par l'utilisateur. Toutefois, seul le premier élément doit être effectué avant la présentation du frame suivant.

  1. Remplacez le texte dans la zone de texte par ce que l'utilisateur a saisi et appliquez la mise en forme requise.
  2. Mettez à jour la partie de l'UI qui affiche le nombre de mots actuel.
  3. Exécutez une logique pour vérifier les fautes d'orthographe.
  4. Enregistrez les modifications les plus récentes (localement ou dans une base de données distante).

Le code à utiliser peut se présenter comme suit:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

La visualisation suivante montre comment différer les mises à jour non critiques jusqu'après le frame suivant peut réduire la durée de traitement et donc la latence d'interaction globale.

Illustration d'une interaction au clavier et des tâches ultérieures dans deux scénarios. Dans la figure du haut, la tâche critique de rendu et toutes les tâches en arrière-plan ultérieures s'exécutent de manière synchrone jusqu'à ce qu'il soit possible de présenter un frame. Dans l'image du bas, le travail critique pour le rendu s'exécute en premier, puis cède la place au thread principal pour présenter un nouveau frame plus tôt. Les tâches en arrière-plan s'exécutent ensuite.
Cliquez sur l'image ci-dessus pour afficher une version haute résolution.

Bien que l'utilisation de setTimeout() dans un appel requestAnimationFrame() dans l'exemple de code précédent soit un peu ésotérique, il s'agit d'une méthode efficace qui fonctionne dans tous les navigateurs pour s'assurer que le code non critique ne bloque pas le frame suivant.

Éviter le thrashing de la mise en page

Le forçage de la mise en page (parfois appelé mise en page synchrone forcée) est un problème de performances de rendu où la mise en page se produit de manière synchrone. Il se produit lorsque vous mettez à jour des styles en JavaScript, puis les lisez dans la même tâche. De nombreuses propriétés JavaScript peuvent entraîner un gaspillage de mise en page.

Visualisation de la fragmentation de la mise en page, comme indiqué dans le panneau "Performances" de Chrome DevTools.
Exemple de surcharge de mise en page, comme indiqué dans le panneau des performances des outils pour les développeurs Chrome. Les tâches de rendu impliquant un épuisement de la mise en page sont signalées par un triangle rouge en haut à droite de la partie de la pile d'appel, souvent intitulée Recalculate Style (Recalculer le style) ou Layout (Mise en page).

Le forçage de mise en page est un goulot d'étranglement des performances, car en mettant à jour les styles, puis en demandant immédiatement les valeurs de ces styles en JavaScript, le navigateur est obligé d'effectuer un travail de mise en page synchrone qu'il aurait pu effectuer de manière asynchrone plus tard, une fois l'exécution des rappels d'événements terminée.

Réduire le délai de présentation

Le délai de présentation d'une interaction s'étend du moment où les rappels d'événement d'une interaction ont fini de s'exécuter jusqu'au moment où le navigateur peut peindre le frame suivant qui affiche les modifications visuelles qui en résultent.

Réduire la taille du DOM

Lorsque le DOM d'une page est petit, le rendu se termine généralement rapidement. Toutefois, lorsque les DOM deviennent très volumineux, le travail de rendu tend à augmenter avec la taille du DOM. La relation entre le travail de rendu et la taille du DOM n'est pas linéaire, mais les DOM volumineux nécessitent plus de travail de rendu que les DOM de petite taille. Un DOM volumineux pose problème dans deux cas:

  1. Lors du rendu initial de la page, où un DOM volumineux nécessite beaucoup de travail pour afficher l'état initial de la page.
  2. En réponse à une interaction utilisateur, où un grand DOM peut entraîner des mises à jour de rendu très coûteuses, et donc augmenter le temps nécessaire au navigateur pour présenter le frame suivant.

N'oubliez pas que, dans certains cas, les DOM volumineux ne peuvent pas être réduits de manière significative. Vous pouvez adopter des approches pour réduire la taille du DOM, comme aplatir le DOM ou ajouter des éléments au DOM lors des interactions utilisateur afin de réduire la taille initiale du DOM, mais ces techniques ne peuvent pas aller très loin.

Utiliser content-visibility pour afficher de manière paresseuse les éléments hors écran

Pour limiter la quantité de travail de rendu lors du chargement de la page et en réponse aux interactions utilisateur, vous pouvez vous appuyer sur la propriété CSS content-visibility, qui revient à afficher de manière paresseuse les éléments à mesure qu'ils s'approchent de la fenêtre d'affichage. Bien que content-visibility puisse nécessiter un peu de pratique pour être utilisé efficacement, il est intéressant de vérifier si le résultat est un temps de rendu plus court qui peut améliorer l'INP de votre page.

Tenir compte des coûts de performances lors de l'affichage du code HTML à l'aide de JavaScript

Lorsqu'il y a du code HTML, il y a une analyse du code HTML. Une fois que le navigateur a terminé d'analyser le code HTML dans un DOM, il doit lui appliquer des styles, effectuer des calculs de mise en page et ensuite afficher cette mise en page. Il s'agit d'un coût inévitable, mais la manière dont vous effectuez le rendu HTML est importante.

Lorsque le serveur envoie du code HTML, il arrive dans le navigateur sous forme de flux. Le streaming signifie que la réponse HTML du serveur arrive par blocs. Le navigateur optimise la gestion d'un flux en analysant de manière incrémentielle les segments de ce flux à mesure qu'ils arrivent et en les affichant bit par bit. Il s'agit d'une optimisation des performances, car le navigateur génère implicitement des rendements périodiquement et automatiquement pendant le chargement de la page.

Bien que la première visite d'un site Web implique toujours une certaine quantité de code HTML, une approche courante commence par un code HTML initial minimal, puis JavaScript est utilisé pour renseigner la zone de contenu. Les mises à jour ultérieures de cette zone de contenu sont également effectuées en fonction des interactions des utilisateurs. C'est ce que l'on appelle généralement le modèle d'application monopage (SPA). L'inconvénient de ce modèle est qu'en affichant du code HTML avec JavaScript côté client, vous n'obtenez pas seulement le coût du traitement JavaScript pour créer ce code HTML, mais le navigateur ne s'arrête pas tant qu'il n'a pas terminé d'analyser ce code HTML et de l'afficher.

Il est toutefois important de se rappeler que même les sites Web qui ne sont pas des SPA impliquent probablement un certain rendu HTML via JavaScript en raison des interactions. Cela est généralement acceptable, à condition que vous n'exécutiez pas de rendu de grandes quantités de code HTML sur le client, ce qui peut retarder la présentation du frame suivant. Toutefois, il est important de comprendre les conséquences sur les performances de cette approche de rendu HTML dans le navigateur et comment elle peut affecter la réactivité de votre site Web aux entrées utilisateur si vous affichez beaucoup de code HTML via JavaScript.

Conclusion

L'amélioration de l'INP de votre site est un processus itératif. Lorsque vous corrigez une interaction lente sur le terrain, il est probable que vous en trouviez d'autres, en particulier si votre site Web propose de nombreuses interactions. Vous devrez alors les optimiser également.

La clé pour améliorer l'INP est la persistance. À terme, vous pourrez atteindre un niveau de réactivité de votre page qui permettra aux utilisateurs d'être satisfaits de l'expérience que vous leur proposez. Il est également probable que, lorsque vous développerez de nouvelles fonctionnalités pour vos utilisateurs, vous deviez suivre le même processus pour optimiser les interactions qui leur sont propres. Cela demandera du temps et des efforts, mais ce sera du temps et des efforts bien dépensés.

Image principale issue de Unsplash, par David Pisnoy et modifiée conformément à la licence Unsplash.