Le WordPress d'aujourd'hui, décodé pour les développeurs

Blocs Gutenberg

Antipatterns : un bloc dynamique interrogé à chaque défilement infini

Un défilement infini mal câblé peut multiplier les rendus d'un bloc dynamique par dizaines. Diagnostic de l'antipattern et correction par une vraie pagination.

Par WordPress Développement • 21 février 2022 • 5 min de lecture • Aucun commentaire
Antipatterns : un bloc dynamique interrogé à chaque défilement infini

Douze appels identiques à la même route REST en moins de trois secondes : c’est ce que montrait le journal réseau d’un bloc « articles liés » construit autour d’un défilement infini un peu trop bavard. Le bloc semblait fonctionner, la liste s’allongeait au fil du défilement, mais chaque pixel parcouru relançait une requête vers le serveur.

Ce genre de bloc dynamique repose sur un render_callback PHP appelé côté serveur puis rafraîchi côté client via apiFetch. Le problème ne vient pas de cette architecture en elle-même, mais de la façon dont le défilement déclenche les rappels : un simple écouteur scroll sans seuil ni verrou peut transformer un bloc de contenu en générateur de requêtes.

Ce qu’on observe sur le terrain

Le symptôme est presque toujours le même : l’onglet réseau du navigateur affiche une rafale de requêtes vers /wp-json/monsite/v1/articles, parfois avec les mêmes paramètres répétés plusieurs fois de suite. Le bloc en question utilise généralement un gestionnaire d’événement posé directement sur window, sans debounce ni comparaison avec l’état précédent :

window.addEventListener('scroll', function () {
  if (window.scrollY + window.innerHeight >= document.body.scrollHeight - 200) {
    chargerArticlesSuivants();
  }
});

À chaque pixel de défilement dans la zone de déclenchement, la fonction se relance. Si l’utilisateur remonte puis redescend sa page, ou si son geste de défilement est saccadé sur un mobile, la condition peut redevenir vraie plusieurs fois avant que la première requête n’ait fini de répondre.

Pourquoi le défilement infini multiplie les rendus

Trois facteurs s’additionnent généralement dans ce type de bloc :

  • Aucun verrou d’état (une variable chargementEnCours qui empêcherait un second appel pendant que le premier est en vol) ;
  • Aucune mémorisation de la dernière page déjà demandée, si bien que le même numéro de page part deux fois ;
  • Un rendu React qui redéclenche un useEffect à chaque changement d’état intermédiaire, y compris quand rien n’a réellement changé pour l’utilisateur.
L'essentiel à retenir : Un observateur de scroll trop sensible relance le rendu du bloc ; La pagination réelle remplace le comptage de pixels ; Le gain se mesure en requêtes évitées, pas en confort visuel

Le bloc dynamique, mauvais candidat au polling silencieux

Un bloc dynamique enregistré avec un render_callback exécute du PHP à chaque affichage de la page qui le contient, puis peut en plus déclencher des requêtes JavaScript après le chargement. Cette double couche est utile pour le contenu qui change souvent, mais elle devient un piège si la partie JavaScript n’a pas de garde-fou : chaque requête relance potentiellement le même travail côté serveur, une exécution de WP_Query, un calcul de métadonnées, parfois une jointure coûteuse sur une taxonomie personnalisée.

Le bloc observé ici ne mettait rien en cache et ne vérifiait pas non plus si la page demandée avait déjà été récupérée. Résultat : sur une session de lecture d’environ dix minutes, le nombre de requêtes réellement nécessaires (huit pages de dix articles) était démultiplié par quarante, pour atteindre plus de trois cents appels identiques ou quasi identiques.

Remplacer l’observateur naïf par une pagination réelle

La correction ne consiste pas à ralentir l’écouteur de défilement à coups de setTimeout, ce qui masque le symptôme sans le traiter. Elle consiste à faire porter la logique de pagination par un état explicite, vérifié avant tout appel :

let pageCourante = 1;
let chargementEnCours = false;
let toutEstCharge = false;

function chargerArticlesSuivants() {
  if (chargementEnCours || toutEstCharge) {
    return;
  }
  chargementEnCours = true;
  wp.apiFetch({ path: `/monsite/v1/articles?page=${pageCourante}` })
    .then((reponse) => {
      if (reponse.articles.length === 0) {
        toutEstCharge = true;
        return;
      }
      afficherArticles(reponse.articles);
      pageCourante += 1;
    })
    .finally(() => {
      chargementEnCours = false;
    });
}

Côté serveur, la route REST doit elle-même s’appuyer sur les paramètres paged et posts_per_page d’une WP_Query classique, plutôt que de renvoyer l’intégralité du contenu candidat au client à charge pour lui de le filtrer.

Un observateur d’intersection plutôt qu’un calcul de pixels

Remplacer l’écouteur scroll par un IntersectionObserver ciblant un élément sentinelle placé en bas de la liste réduit encore le nombre de déclenchements, puisque le navigateur ne notifie l’observateur que lorsque l’élément entre réellement dans la zone visible, et non à chaque défilement.

Ce que la correction change concrètement

IndicateurAvant correctionAprès correction
Requêtes sur dix minutes de lecture3608
Requêtes en doublon3400
Temps de réponse perçu au défilementIrrégulierStable

Un bloc qui charge du contenu au défilement doit toujours pouvoir répondre à la question « quelle page ai-je déjà demandée ? » avant de répondre à la question « dois-je en demander une nouvelle ? ».

En résumé

Le défilement infini n’est pas fautif en lui-même ; c’est l’absence de verrou d’état et de pagination réelle qui transforme un bloc dynamique en générateur de requêtes redondantes. Un état explicite (page courante, chargement en cours, fin de liste atteinte) suffit à ramener un bloc de trois cents requêtes inutiles à une poignée d’appels strictement nécessaires, sans toucher au cache de fragment ni à la configuration serveur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi