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 ce qui cause une mauvaise INP

Avant de pouvoir corriger les interactions lentes, vous avez besoin 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 de champ d'un fournisseur Real User Monitoring (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 responsable 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 de touche ou appui) et d'autres informations précieuses.

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 flux utilisateur courants et le test des interactions en cours de route, ainsi que les interactions avec la page pendant le chargement (lorsque le thread principal est souvent le plus actif) afin d'identifier les interactions lentes au cours de 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 du traitement, qui correspond au temps nécessaire pour que les rappels d'événement s'exécutent jusqu'à la fin.
  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 dans un certain temps à la latence totale des interactions. Il est donc important de savoir comment optimiser chaque partie de l'interaction pour qu'elle s'exécute le moins de temps 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 des autres activités effectuées sur la page, les délais de saisie peuvent être très longs. Cela peut être dû à une activité sur le thread principal (peut-être en raison du chargement, de l'analyse et de la compilation de scripts), à la gestion de la récupération, 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 réduire au minimum ce délai d'entrée afin que les interactions puissent commencer à exécuter des rappels d'événements 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é récupéré sur le réseau, le navigateur a encore du travail avant que le code JavaScript puisse s'exécuter. Cela inclut l'analyse d'un script pour s'assurer que sa syntaxe est valide, sa compilation en bytecode, puis son exécution.

Selon la taille d'un script, ce travail peut introduire de longues tâches dans le thread principal, ce qui retarde le navigateur pour qu'il réponde 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énements

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 que réduire légèrement le travail qu'ils 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 saisissez du texte, mais qui met également à jour d'autres aspects de l'interface utilisateur en fonction de ce que vous avez écrit (comme le nombre de mots, la mise en évidence des fautes d'orthographe et d'autres commentaires visuels importants). En outre, il se peut que l'application doive également enregistrer ce que vous avez écrit afin de ne pas perdre votre travail si vous la quittez 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 la logique pour vérifier les fautes d'orthographe.
  4. Enregistrez les modifications les plus récentes (en local 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 la figure 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 mise en page

Le thrashing de mise en page, parfois appelé mise en page synchrone forcée, est un problème de performances d'affichage dans lequel 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 thrashing de mise en page est un goulot d'étranglement des performances. En effet, en mettant à jour les styles, puis en demandant immédiatement les valeurs de ces styles en JavaScript, le navigateur est obligé d'effectuer une mise en page synchrone qu'il aurait pu attendre par la suite pour s'exécuter de manière asynchrone après l'exécution des rappels d'événements.

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 grand DOM 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 effectuer un rendu différé des é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 nécessite un peu de pratique pour être utilisé efficacement, il est intéressant de vérifier si le temps de rendu est plus court et 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 du code HTML est importante.

Lorsque le serveur envoie du code HTML, il arrive dans le navigateur sous la forme d'un 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 sur le client, vous obtenez non seulement le coût du traitement JavaScript nécessaire à la création de ce code HTML, mais le navigateur n'est également pas productif tant qu'il n'a pas fini d'analyser et d'afficher ce code HTML.

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 offre de nombreuses interactions. Vous devrez alors les optimiser également.

La clé pour améliorer INP est la persistance. À terme, vous pourrez améliorer la réactivité de votre page de sorte que les utilisateurs soient 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.