Pourquoi certains modules chargent du CSS et du JS sur chaque page

388 vues

La cause profonde : comment le système de hooks de PrestaShop gère les ressources

Si vous avez déjà inspecté le code source de votre boutique PrestaShop et vous êtes demandé pourquoi un module qui n'affiche un widget que sur les pages produits charge ses CSS et JavaScript sur votre page d'accueil, vos pages catégories, et même votre tunnel de commande, vous n'êtes pas seul. C'est l'un des problèmes de performance les plus courants dans l'écosystème PrestaShop, et il découle du fonctionnement du système de hooks combiné à des pratiques de développement négligentes.

PrestaShop utilise une architecture basée sur les hooks pour permettre aux modules d'injecter du contenu, des ressources et de la logique dans différentes parties d'une page. Lorsqu'un module a besoin d'ajouter des fichiers CSS ou JavaScript à la page, il le fait généralement via l'un de ces deux hooks : displayHeader ou actionFrontControllerSetMedia. Ces deux hooks se déclenchent sur chaque page du front office sans exception. Il n'existe aucun mécanisme intégré dans le système de hooks lui-même pour restreindre un appel de hook à des types de pages spécifiques. Il est entièrement de la responsabilité du développeur du module de vérifier quelle page est en cours de chargement et de décider s'il faut ajouter des ressources ou non.

Le problème est que de nombreux développeurs de modules prennent des raccourcis. Au lieu d'écrire une logique conditionnelle qui vérifie le contrôleur actuel, ils enregistrent simplement leurs ressources à chaque appel de hook. Le raisonnement est simple : si les ressources sont toujours chargées, le module fonctionnera toujours, peu importe où son contenu apparaît. Cette approche « tire et oublie » garantit que le module fonctionne correctement, mais elle garantit également de mauvaises performances.

Comment l'abus du hook displayHeader crée de la surcharge de page

Le hook displayHeader est le mécanisme principal par lequel les modules ajoutent du contenu à la section head du HTML de chaque page. Lorsqu'un module implémente la méthode hookDisplayHeader, PrestaShop appelle cette méthode à chaque chargement de page du front office. À l'intérieur de cette méthode, les modules appellent généralement $this->context->controller->addCSS() et $this->context->controller->addJS() pour enregistrer leurs fichiers de ressources.

Voici ce qui se passe dans une boutique typique avec 25 modules installés. Quinze de ces modules ont une méthode hookDisplayHeader. Chacun ajoute entre un et quatre fichiers CSS et JavaScript. Cela signifie que chaque page de votre boutique charge 15 à 60 requêtes HTTP supplémentaires rien que pour les modules, que ces modules affichent ou non quoi que ce soit sur la page actuelle.

Prenons un exemple concret. Un module qui ajoute un popup de guide des tailles sur les pages produits a besoin d'un fichier CSS pour le style du popup et d'un fichier JavaScript pour le comportement du popup. Si le développeur enregistre ces ressources dans hookDisplayHeader sans aucune vérification conditionnelle, ces deux fichiers se chargent sur la page d'accueil, sur chaque page catégorie, sur la page panier, sur la page de commande et sur chaque page CMS. Le guide des tailles n'apparaîtra jamais sur aucune de ces pages, mais le navigateur télécharge, analyse et traite quand même les fichiers CSS et JavaScript.

Multipliez cela par chaque module qui suit le même schéma, et vous commencez à comprendre pourquoi certaines boutiques PrestaShop chargent 2 Mo ou plus de ressources de modules sur des pages où la plupart de ces ressources sont totalement inutiles.

Le hook actionFrontControllerSetMedia

PrestaShop 1.7 a introduit le hook actionFrontControllerSetMedia comme une alternative plus moderne à displayHeader pour l'enregistrement des ressources. Ce hook a été conçu spécifiquement pour enregistrer les fichiers CSS et JavaScript en utilisant les nouvelles méthodes registerStylesheet et registerJavascript. Ces méthodes offrent des avantages par rapport aux anciennes fonctions addCSS et addJS, notamment la possibilité de définir une priorité de chargement, de spécifier des attributs async ou defer, et de contrôler la position (head ou bottom) des balises de ressources.

Cependant, actionFrontControllerSetMedia souffre exactement du même problème fondamental que displayHeader : il se déclenche sur chaque page. Un module qui enregistre des ressources dans hookActionFrontControllerSetMedia sans vérifier le contrôleur actuel charge ces ressources partout, tout comme l'ancienne approche.

La seule différence est la méthode d'enregistrement, pas la portée du chargement. Qu'un développeur utilise addCSS dans displayHeader ou registerStylesheet dans actionFrontControllerSetMedia, le résultat est le même s'il n'ajoute pas de logique conditionnelle. Les ressources se chargent globalement.

Chargement conditionnel approprié : ce que font les bons modules

Un module bien développé vérifie quelle page le client consulte avant de charger des ressources. La méthode standard pour faire cela dans PrestaShop est de vérifier le nom du contrôleur. Chaque page du front office est servie par un contrôleur spécifique : IndexController pour la page d'accueil, CategoryController pour les pages catégories, ProductController pour les pages produits, CartController pour le panier, et ainsi de suite.

À l'intérieur de la méthode hookDisplayHeader ou hookActionFrontControllerSetMedia, le développeur peut accéder au contrôleur actuel via $this->context->controller. En vérifiant le nom de la classe ou la propriété php_self du contrôleur, le module peut décider s'il doit charger ses ressources. Un module d'avis produits, par exemple, devrait vérifier si la page actuelle est une page produit et ne charger son CSS et son JavaScript que dans ce cas.

Cette approche conditionnelle n'est pas difficile à implémenter. Elle ajoute peut-être cinq à dix lignes de code à la méthode du hook. Pourtant, un nombre surprenant de modules, y compris des modules payants populaires sur la marketplace PrestaShop Addons et des modules gratuits bien connus, ne font absolument pas cette vérification. La raison est une combinaison de commodité pour le développeur et du fait que PrestaShop n'impose ni même n'encourage le chargement conditionnel dans sa documentation de développement de modules.

Certains développeurs arguent que charger les ressources globalement garantit la compatibilité avec les thèmes personnalisés ou les configurations de pages inhabituelles où le contenu du module pourrait apparaître de manière inattendue. Bien que cet argument ait une part de vérité, il ne justifie pas le coût en performance. Une meilleure approche est de charger les ressources conditionnellement par défaut et de fournir une option de configuration pour activer le chargement global pour les boutiques qui en ont besoin.

La méthode setMedia : bonnes pratiques pour les développeurs de modules

Pour les développeurs de modules qui lisent cet article, voici les bonnes pratiques pour le chargement des ressources qui respectent les performances du site de vos utilisateurs.

Premièrement, utilisez toujours le hook actionFrontControllerSetMedia au lieu de displayHeader pour l'enregistrement des ressources dans PrestaShop 1.7 et versions ultérieures. Le hook plus récent offre un meilleur contrôle sur le comportement de chargement des ressources et sépare l'enregistrement de vos ressources de la sortie HTML.

Deuxièmement, vérifiez toujours le contrôleur actuel avant d'enregistrer des ressources. Utilisez une approche par liste blanche : définissez la liste des contrôleurs où votre module affiche du contenu et n'enregistrez les ressources que lorsque le contrôleur actuel est dans cette liste. C'est plus maintenable qu'une approche par liste noire car les nouveaux types de pages ajoutés dans les futures versions de PrestaShop ne chargeront pas accidentellement vos ressources.

Troisièmement, utilisez registerStylesheet et registerJavascript au lieu de addCSS et addJS. Les méthodes plus récentes vous permettent de spécifier un identifiant pour chaque ressource, ce qui rend possible pour les thèmes et autres modules de remplacer ou supprimer vos ressources proprement. Elles prennent également en charge les paramètres de priorité qui contrôlent l'ordre de chargement des ressources.

Quatrièmement, demandez-vous si votre JavaScript a vraiment besoin d'être chargé dans le head ou s'il peut être chargé en bas de page avec l'attribut defer. Le JavaScript dans le head bloque le rendu, ce qui signifie que le navigateur ne peut afficher aucun contenu tant que votre fichier n'a pas fini d'être téléchargé et analysé. Déplacer les fichiers en bas de page ou ajouter defer élimine ce comportement de blocage du rendu.

Cinquièmement, minimisez la taille de vos fichiers de ressources. Minifiez vos fichiers CSS et JavaScript avant de distribuer votre module. Supprimez les règles CSS inutilisées. Évitez d'inclure des bibliothèques entières comme jQuery UI ou Bootstrap quand vous n'utilisez qu'une petite partie de leurs fonctionnalités. Chaque kilo-octet compte quand les ressources de votre module se chargent sur des milliers de pages vues par jour.

Mesurer l'impact sur les performances du chargement global des ressources

Pour comprendre combien le chargement global des ressources coûte à votre boutique, vous avez besoin de mesures concrètes. Ouvrez les DevTools de Chrome sur votre page d'accueil et allez dans l'onglet Réseau. Rechargez la page et filtrez les requêtes par le chemin /modules/. Comptez le nombre total de requêtes et leur taille combinée. C'est la surcharge de ressources de modules sur une page où la plupart des modules n'affichent aucun contenu.

Faites maintenant la même chose sur une page produit, où de nombreux modules ont légitimement besoin de leurs ressources. Comparez les chiffres. Sur une boutique bien optimisée, la page produit devrait charger nettement plus de ressources de modules que la page d'accueil parce que c'est là que la plupart des modules affichent leur contenu. Sur une boutique mal optimisée, les chiffres sont quasiment identiques parce que chaque module charge tout partout.

Un benchmark concret issu d'audits réels : une boutique avec 35 modules installés chargeait 1,8 Mo de CSS et JavaScript de modules sur la page d'accueil. Après avoir implémenté le chargement conditionnel sur tous les modules, les ressources de modules de la page d'accueil sont tombées à 340 Ko. La page produit, où la plupart des modules avaient légitimement besoin de leurs ressources, est passée de 2,1 Mo à 1,4 Mo. Le temps de chargement de la page d'accueil s'est amélioré de 1,3 seconde et le score de performance Lighthouse est passé de 42 à 71.

Ces chiffres ne sont pas inhabituels. Le chargement global des ressources est l'une des plus grandes sources de poids de page inutile dans les boutiques PrestaShop, et le corriger produit souvent les améliorations de performance les plus spectaculaires.

Comment identifier les modules fautifs dans votre boutique

Trouver quels modules chargent des ressources globalement nécessite une approche systématique. Commencez avec l'onglet Réseau dans les DevTools comme décrit ci-dessus. Pour chaque ressource de module que vous trouvez en cours de chargement sur la page d'accueil, posez-vous la question : est-ce que ce module affiche du contenu sur la page d'accueil ? Si la réponse est non, ce module charge des ressources inutilement.

Une autre approche consiste à utiliser le mode de profilage debug de PrestaShop. Lorsque le profilage est activé (en réglant _PS_DEBUG_PROFILING_ sur true dans votre config/defines.inc.php), PrestaShop affiche des données détaillées d'exécution des hooks en bas de chaque page. Ces données vous montrent exactement quels modules s'exécutent sur chaque hook et combien de temps ils prennent. Tout module qui s'exécute sur displayHeader ou actionFrontControllerSetMedia mais ne s'exécute sur aucun hook d'affichage qui produit une sortie visible sur la page actuelle est un candidat à l'optimisation.

Vous pouvez également vérifier de manière programmatique en examinant le code source de chaque module. Regardez les méthodes hookDisplayHeader et hookActionFrontControllerSetMedia dans le fichier PHP principal du module. Si la méthode contient des appels addCSS, addJS, registerStylesheet ou registerJavascript sans aucune vérification conditionnelle sur le nom du contrôleur, ce module charge des ressources globalement.

Pour les boutiques avec de nombreux modules, cette revue manuelle peut être chronophage. Un raccourci pratique est de se concentrer d'abord sur les ressources les plus volumineuses. Triez les ressources de modules dans l'onglet Réseau par taille et commencez à investiguer les fichiers les plus gros. Un fichier JavaScript de 200 Ko chargé globalement est un problème bien plus grave qu'un fichier CSS de 3 Ko, alors priorisez en conséquence.

Demander des corrections aux développeurs de modules

Lorsque vous identifiez un module qui charge des ressources globalement, votre première étape devrait être de contacter le développeur et de demander une correction. La plupart des développeurs de modules professionnels sont réceptifs aux demandes d'amélioration des performances, surtout lorsque vous pouvez fournir des données spécifiques sur l'impact.

Rédigez un message clair et technique qui explique le problème. Mentionnez que la méthode hookDisplayHeader du module charge du CSS et du JavaScript sur chaque page sans vérifier le contrôleur actuel. Précisez quelles pages ont réellement besoin des ressources et quelles pages les chargent inutilement. Incluez les tailles de fichiers et l'impact estimé sur les performances. Si vous avez des scores Lighthouse montrant l'avant et l'après lorsque les ressources du module sont bloquées, incluez-les.

Si le développeur est réactif, il publiera généralement une mise à jour dans les quelques semaines qui suivent, ajoutant le chargement conditionnel. Si le développeur est injoignable ou rejette la préoccupation, vous avez plusieurs options : implémenter le chargement conditionnel vous-même en éditant le code du module, utiliser un module de performance qui peut bloquer sélectivement les ressources sur des pages spécifiques, ou trouver un module alternatif qui suit de meilleures pratiques de développement.

Solutions de contournement quand vous ne pouvez pas modifier le module

Parfois, vous ne pouvez pas modifier un module directement. Il peut être chiffré avec IonCube, il peut s'agir d'un module dont vous ne voulez pas maintenir un fork, ou le module peut écraser vos modifications à chaque mise à jour. Dans ces cas, vous avez besoin de solutions de contournement.

Une solution de contournement efficace est de créer un petit module personnalisé qui décroche le module fautif de displayHeader et actionFrontControllerSetMedia, puis réajoute les ressources uniquement sur les pages où elles sont nécessaires. Ce module personnalisé utiliserait le hook actionFrontControllerSetMedia avec une priorité élevée (pour qu'il s'exécute après le module fautif) et appellerait Media::unregisterStylesheet et Media::unregisterJavascript pour supprimer les ressources chargées globalement. Puis, sur les pages où les ressources sont réellement nécessaires, il les réenregistrerait.

Une autre solution est d'utiliser le fichier Theme.yml pour remplacer les ressources des modules. Dans PrestaShop 1.7 et versions ultérieures, le fichier de configuration theme.yml du thème peut empêcher le chargement de ressources spécifiques de modules. C'est une solution au niveau du thème qui persiste à travers les mises à jour des modules. Cependant, elle supprime les ressources de toutes les pages, pas sélectivement, donc vous devriez la combiner avec un réajout des ressources sur des pages spécifiques via un module personnalisé ou un template de thème.

Une troisième option, disponible dans PrestaShop 8.x, est d'utiliser les fonctionnalités intégrées de priorité et de suppression du système de gestion des ressources. PrestaShop 8 a amélioré le pipeline des ressources pour donner aux thèmes plus de contrôle sur quelles ressources de modules sont chargées. Consultez la documentation de votre thème pour des instructions spécifiques sur la façon d'exploiter ces fonctionnalités.

Quelle que soit l'approche que vous choisissez, testez toujours minutieusement après avoir effectué des modifications. Supprimer le CSS d'un module peut casser son apparence visuelle, et supprimer son JavaScript peut casser les fonctionnalités interactives. Testez chaque type de page où le module devrait encore fonctionner correctement.

Prévention : évaluer les modules avant l'installation

La meilleure façon d'éviter les problèmes de chargement global des ressources est d'évaluer les modules avant de les installer. Bien que cela ne soit pas toujours possible, il existe des vérifications que vous pouvez effectuer.

Si le module fournit une boutique de démonstration, visitez-la et inspectez la page d'accueil avec les DevTools. Vérifiez si les ressources du module se chargent sur les pages où le module n'affiche pas de contenu. Si les ressources se chargent globalement sur la démo, elles se chargeront globalement sur votre boutique aussi.

Si vous avez accès au code source du module avant l'achat (certaines marketplaces fournissent des aperçus du code), examinez les méthodes hookDisplayHeader et hookActionFrontControllerSetMedia. Vérifiez la présence d'une logique de chargement conditionnel. L'absence de toute vérification du contrôleur est un signal d'alarme.

Lisez les avis et le forum de support du module. Les utilisateurs soucieux des performances signalent souvent les problèmes de chargement global des ressources dans les avis. Si plusieurs utilisateurs se sont plaints du ralentissement de leur boutique par le module, prenez ces retours au sérieux.

Enfin, considérez la qualité globale du code du développeur. Les développeurs qui suivent les bonnes pratiques dans un domaine ont tendance à les suivre dans les autres. Si le code d'un module est propre, bien documenté et respecte les normes de codage de PrestaShop, il est plus probable qu'il gère correctement le chargement des ressources. Si le code est désordonné et mal structuré, le chargement global des ressources n'est probablement qu'un problème parmi d'autres.

Cette réponse vous a-t-elle été utile ?

Vous avez encore des questions ?

Can't find what you're looking for? Send us your question and we'll get back to you quickly.

Loading...
Back to top