-

Comment améliorer les performances d'un site Web avec le module BigPipe de Drupal ? Un guide complet

La vitesse du site est cruciale, particulièrement de nos jours, où les solutions de gestion de contenu modernes sont plus dynamiques et interactives. L'approche traditionnelle de diffusion des pages est notoirement inefficace dans ce contexte. De nombreuses techniques existent pour atteindre des performances optimales, et l'une de ces méthodes est la technique BigPipe, initialement développée chez Facebook. La bonne nouvelle, c'est que le module BigPipe, qui intègre la même fonctionnalité, a été intégré au cœur de Drupal 8 depuis la version 8.1.

Comment fonctionne Drupal BigPipe ?

L'idée générale de la technique BigPipe est de décomposer les pages web en petits morceaux appelés pagelets et de les acheminer à travers plusieurs étapes d'exécution à l'intérieur des serveurs web et des navigateurs. 

À un niveau élevé, BigPipe envoie une réponse HTML en morceaux :

1. Un morceau : tout jusqu'à juste avant </body> - cela contient des espaces réservés BigPipe pour les parties personnalisées de la page. Cela envoie donc les parties non personnalisées de la page. Appelons cela Le Squelette.

2. N morceaux: une balise <script> par espace réservé BigPipe dans Le Squelette.

3. Un morceau : </body>  et tout ce qui suit.

C'est conceptuellement identique à BigPipe de Facebook (d'où le nom).

En quoi Drupal BigPipe diffère-t-il de la technique de Facebook ?

Le module Drupal diffère sensiblement de la mise en œuvre de Facebook (et d'autres) par sa capacité à déterminer automatiquement quelles parties de la page web peuvent bénéficier d'une diffusion à la BigPipe. 

Le système de rendu de Drupal repose sur le concept de “prépagination automatique.” Qu'est-ce que cela signifie ? Le contenu trop dynamique est remplacé par un espace réservé qui peut être rendu plus tard. 

En outre, il dispose également du concept de “stratégies d'espaces réservés.” Par défaut, les espaces réservés sont remplacés côté serveur, et la réponse est bloquée tant que tous ne sont pas remplacés. Mais il est possible d'ajouter des stratégies d'espaces réservés supplémentaires. BigPipe n'est qu'une des options. D'autres peuvent être ESI, AJAX, etc. 

BigPipe mis en œuvre par Facebook, ne peut fonctionner que si JavaScript est activé. En revanche, le module Drupal BigPipe permet de remplacer les espaces réservés sans JavaScript “no-JS BigPipe.” Ce n'est pas techniquement BigPipe du tout, mais juste l'utilisation de plusieurs vidanges. 

Cela nous permet d'utiliser à la fois no-JS BigPipe et “BigPipe classique” dans la même réponse pour maximiser la quantité de contenu que nous pouvons envoyer dès que possible.

Donc, essentiellement, cela se produit pendant le processus de rendu de la page :

  • Les parties personnalisées sont transformées en espaces réservés
    <span data-big-pipe-placeholder-id="callback=d_profiles.builder%3Abuild&amp;args%5B0%5D=profile_widget_mini
    &amp;token=6NHeAQvXLYdzXuoWp2TRCvedTO2WAoVKnpW-5_pV9gk"></span>.
    L'espace réservé contient des informations sur quel rappel appeler et les arguments à lui passer. Le moteur de rendu continue ensuite à parcourir le tableau et à le convertir en HTML. Le HTML résultant, y compris les espaces réservés, est mis en cache. Ensuite, selon la stratégie de rendu utilisée, les espaces réservés sont chacun remplacés par leur contenu dynamique.
  • Le remplacement des espaces réservés se fait à l'aide de JavaScript. Le rappel commence à chercher des éléments de script de remplacement une fois qu'un élément spécial <script type="application/vnd.drupal-ajax" data-big-pipe-event="stop"> est imprimé et trouvé.
  • Au tout dernier moment, il est remplacé par le contenu réel. Cette nouvelle stratégie nous permet de vider la page web initiale d'abord puis de diffuser les remplacements pour les espaces réservés.

Quand utiliser un constructeur paresseux ?

En règle générale, vous devriez envisager d'utiliser un constructeur paresseux chaque fois que le contenu que vous ajoutez à un tableau de rendu est d'un des types suivants.

  • Contenu qui aurait une haute cardinalité s'il était mis en cache. Par exemple, un bloc affichant le nom de l'utilisateur. Il peut être mis en cache, mais comme il varie selon l'utilisateur, cela aboutira probablement également à des objets mis en cache avec un faible taux d'accès.
  • Contenu qui ne peut pas être mis en cache ou qui a un taux d'invalidation très élevé. Par exemple, l'affichage de la date/heure actuelle, ou des statistiques qui doivent toujours être les plus à jour possible.
  • Contenu qui nécessite un processus d'assemblage long et potentiellement lent. Par exemple, un bloc affichant le contenu d'une API tierce où demander du contenu à l'API entraîne des frais généraux.

Utiliser des constructeurs paresseux en pratique

Pour fournir un exemple complet de mise en œuvre de constructeurs paresseux, considérons un scénario avec un bloc de widget de profil personnalisé placé sur une page web. Le bloc contient la photo de l'utilisateur, son nom complet et son menu de profil.

Un widget de profil personnalisé sur un site de test fourni pour utiliser le module Drupal BigPipe.

Afin de garantir que chaque utilisateur voit ses informations personnalisées, nous pouvons mettre en œuvre des stratégies spécifiques, telles que le réglage de "l'âge maximal" à 0 ou l'utilisation de contextes utilisateur et de balises. Cependant, il est important de noter que la configuration de "l'âge maximal" à 0 entraînera le fait que le reste de la page web ne sera pas mis en cache.

Grâce au concept de “prépagination automatique,” Drupal considère ce bloc comme une partie personnalisée et le transforme en un espace réservé.

Le seul problème ici est que nous avons tout le bloc remplacé par un espace réservé par la suite :

<span data-big-pipe-placeholder-id="callback=Drupal%5Cblock%5CBlockViewBuilder%3A%3AlazyBuilder&amp;args%5B0%5D
=profilewidget&amp;args%5B1%5D=full&amp;args%5B2%5D&amp;token=QzMTPnxwihEGO
itjJB_tahJj8V-L-KopAVnEjVEMSsk"></span>

Néanmoins, il convient de noter que certaines données au sein du bloc pourraient rester statiques ou constantes pour tous les utilisateurs, comme ceci :

Un bloc de widget de profil avec des éléments statiques et constants pour tester le module Drupal BigPipe.

Pour rendre notre bloc plus granulaire, nous pouvons transformer les parties dynamiques en espaces réservés tandis que le contenu du bloc restant reste mis en cache et se charge lors du chargement initial de la page.

Étape 1. Création de constructeurs paresseux

Les constructeurs paresseux sont implémentés en utilisant le tableau de rendu du type #lazy_builder, comme les autres éléments. Le tableau de rendu doit contenir un rappel comme premier élément et un tableau d'arguments pour ce rappel comme deuxième élément.

Les éléments de rendu des constructeurs paresseux ne doivent contenir que les propriétés #cache, #weight et #create_placeholder

public function build() {

 $build['user_data'] = [

   '#lazy_builder' => [

     'd_profiles.builder:build',

     [],

   ],

   '#create_placeholder' => TRUE,

 ];



 $build['user_menu'] = $this->buildMenu()



 $build['user_img'] = [

   '#lazy_builder' => [

     'd_profiles.builder:build',

     ['profile_widget_mini'],

   ],

   '#create_placeholder' => TRUE,

 ];



 return $build;

}


Étape 2. Mise en œuvre de TrustedCallbackInterface

Avant d'aller plus loin, nous devons nous assurer que notre implémentation de constructeur paresseux peut appeler la méthode lazyBuilder(). Pour ce faire, nous devons implémenter le TrustedCallbackInterface pour dire à Drupal que notre rappel de constructeur paresseux est autorisé à être appelé.

Lors de l'implémentation de cette interface, nous devons ajouter une méthode appelée trustedCallbacks(), qui sera appelée automatiquement par Drupal grâce à la détection de l'interface. La valeur de retour de cette méthode doit être toutes les méthodes de cette classe qui peuvent être utilisées comme rappels.

Voici l'implémentation de base de cette fonctionnalité pour notre bloc :

/**
* Fournit un constructeur paresseux pour le bloc de profil.
*/
class ProfileBuilder implements TrustedCallbackInterface {

 /**
  * {@inheritdoc}
  */
 public static function trustedCallbacks() {
   return ['build'];
 }

 /**
  * Construire les détails du profil.
  */
 public function build($view_mode = 'navbar_widget') {
   return $this->entityBuilder->build($this->loadProfile(), $view_mode);
 }
}

En conséquence, le bloc mis en cache ressemblera à :

<div id="profile-widget" class="profile-widget dropdown">
  <button class="btn dropdown-toggle" type="button" id="dropdown-menu-button" data-toggle="dropdown" aria-expanded="false">       
    <div id="block-profilewidget" class="img img--round">
      <span data-big-pipe-placeholder-id="callback=em_profiles.builder%3Abuild&amp;args%5B0%5D=profile_widget_mini&amp;token=ArkAzE-rR2gaSeRCkyb61vLT6nWbvDcIx0HQ8gjUMUs"></span>
    </div>
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdown-menu-button">
    <section class="profile-widget__data p-5">
      <h3 class="profile-widget__name title-h4 mb-2">Profil</h3>
      <span data-big-pipe-placeholder-id="callback=em_profiles.builder%3Abuild&amp;&amp;token=ODDkF_Laqrq9ERh-djJN_UI_C1J2L6FtmRMh8luWPqk"></span>
    </section>
    <nav class="profile-widget__menu p-5" aria-labelledby="profile-widget">        
      <ul class="nav navbar-nav">
        <li class="nav-item">
          <a href="/user/settings" class="nav-link--icon-settings nav-link--icon nav-link" data-drupal-link-system-path="user/settings">Paramètres</a>
        </li>
        <li class="nav-item">
          <a href="/user/logout" class="nav-link--icon nav-link--icon-logout nav-link" data-drupal-link-system-path="user/logout">Déconnexion</a>
        </li>
      </ul>
    </nav>
  </div>
</div>

Normalement, le rappel du constructeur paresseux sera exécuté à chaque chargement de page, ce qui est le comportement prévu. Mais dans certains cas, il peut également être nécessaire de mettre en cache les espaces réservés. Pour ce faire, nous devons inclure des clés de cache ainsi que le contexte de cache, comme dans l'exemple ci-dessous :

$build['user_data'] = [

  '#lazy_builder' => [

    'em_profiles.builder:build',

    [],

   ],

  '#create_placeholder' => TRUE,

  '#cache' => [

    'contexts' => ['user'],

    'keys' => [

      'entity_view',

      'user',

     'profile',

     'navbar_widget',

    ],

  ],

];


Étape 3. Assurer une expérience de chargement de page visuelle fluide

Étant donné que Drupal BigPipe charge paresseusement certaines parties de la page, cela pourrait entraîner une expérience de chargement de page saccadée. Cela dépend de notre thème et de l'emplacement du contenu chargé paresseusement.

La solution la plus simple est de faire apparaître le contenu chargé paresseusement dans un espace qui leur est réservé pour éviter de reformer le contenu. Alternativement, nous pouvons appliquer une animation de “chargement” à tous les espaces réservés BigPipe dans notre thème avec un peu de CSS.

La dernière option possible est de définir un aperçu de l'interface qui sera peuplé par BigPipe, en utilisant un modèle Twig.

Comparons le résultat final de la stratégie de constructeur paresseux personnalisé (1) contre la stratégie de “prépagination automatique” (2).

Video file

Stratégie de constructeur paresseux personnalisé (1)
 

Un résultat de l'utilisation de la stratégie

Stratégie de “prépagination automatique” de Drupal (2)

Les deux stratégies fonctionnent bien, mais vous pouvez voir les inconvénients de la prépagination automatique, tels que l'expérience de chargement de page saccadée (changement drastique de mise en page).

Plus d'exemples :

1. Bloc de statistiques avec partie statique chargée immédiatement et contenu dynamique chargé plus tard :

--


2. Vues avec squelette :

-


Dépannage des constructeurs paresseux

Si vous avez implémenté un constructeur paresseux et qu'il ne permet pas d'accélérer le chargement de votre page Drupal ou ne fonctionne tout simplement pas comme prévu, il y a quelques points à vérifier :

  • Assurez-vous que le module Drupal BigPipe est actif. 
  • Vérifiez les paramètres de cache de votre méthode de rappel du constructeur paresseux. Par défaut, Drupal fera des suppositions sur la façon dont il doit être mis en cache, ce qui n'est pas toujours correct pour votre cas d'utilisation. Vous pouvez au contraire définir explicitement les paramètres de cache.
  • Une couche CDN ou Varnish en amont pourrait mettre en cache l'intégralité de votre page web, de sorte que toute la sortie du processus de rendu BigPipe sera servie simultanément. Vous aurez besoin de trouver un autre mécanisme pour contourner cela.

Drupal BigPipe - résumé 

Dans cet article, nous avons exploré une stratégie de rendu alternative qui nous permet de différer le rendu du contenu hautement dynamique seulement après que les parties statiques de la page web ont déjà été chargées depuis le cache.

BigPipe, le module Drupal, peut automatiquement améliorer les performances de notre site web grâce à un pipeline de rendu et une API de rendu améliorés, en particulier les métadonnées de mise en cache et la prépagination automatique.

Cependant, il est essentiel de noter que l'utilisation de Drupal BigPipe ne remplace pas le traitement des problèmes de performances sous-jacents. La mise en œuvre de constructeurs paresseux pour atténuer l'impact du code lent sur votre page web ne fera que masquer le problème au lieu de le résoudre entièrement. En mettant en œuvre ces techniques de manière efficace, vous pouvez optimiser les performances et enrichir l'expérience utilisateur globale sur vos sites web propulsés par Drupal.

As part of Drupal support, we maintain existing websites and expand them with new functionalities