Warum manche Module CSS und JS auf jeder Seite laden
Die Ursache: Wie PrestaShops Hook-System Assets verarbeitet
Wenn Sie jemals den Quellcode Ihres PrestaShop-Shops untersucht haben und sich gefragt haben, warum ein Modul, das nur ein Widget auf Produktseiten anzeigt, seine CSS- und JavaScript-Dateien auf Ihrer Startseite, Ihren Kategorieseiten und sogar in Ihrem Checkout lädt, sind Sie nicht allein. Dies ist eines der häufigsten Leistungsprobleme im PrestaShop-Ökosystem, und es resultiert aus der Funktionsweise des Hook-Systems in Kombination mit nachlässigen Entwicklungspraktiken.
PrestaShop verwendet eine Hook-basierte Architektur, um Modulen zu ermöglichen, Inhalte, Assets und Logik in verschiedene Teile einer Seite einzufügen. Wenn ein Modul CSS- oder JavaScript-Dateien zur Seite hinzufügen muss, tut es dies typischerweise über einen von zwei Hooks: displayHeader oder actionFrontControllerSetMedia. Beide Hooks werden auf jeder Front-Office-Seite ausnahmslos ausgelöst. Es gibt im Hook-System selbst keinen eingebauten Mechanismus, um einen Hook-Aufruf auf bestimmte Seitentypen zu beschränken. Es liegt vollständig in der Verantwortung des Modulentwicklers, zu prüfen, welche Seite geladen wird, und zu entscheiden, ob Assets hinzugefügt werden sollen oder nicht.
Das Problem ist, dass viele Modulentwickler Abkürzungen nehmen. Anstatt bedingte Logik zu schreiben, die den aktuellen Controller prüft, registrieren sie ihre Assets einfach bei jedem Hook-Aufruf. Die Begründung ist simpel: Wenn die Assets immer geladen werden, funktioniert das Modul immer, unabhängig davon, wo sein Inhalt erscheint. Dieser „Fire-and-Forget"-Ansatz garantiert, dass das Modul korrekt funktioniert, aber er garantiert ebenso eine schlechte Leistung.
Wie displayHeader-Hook-Missbrauch Seiten aufbläht
Der displayHeader-Hook ist der primäre Mechanismus, über den Module Inhalte zum HTML-Head-Bereich jeder Seite hinzufügen. Wenn ein Modul die hookDisplayHeader-Methode implementiert, ruft PrestaShop diese Methode bei jedem Front-Office-Seitenaufruf auf. Innerhalb dieser Methode rufen Module typischerweise $this->context->controller->addCSS() und $this->context->controller->addJS() auf, um ihre Asset-Dateien zu registrieren.
So sieht es in einem typischen Shop mit 25 installierten Modulen aus. Fünfzehn dieser Module haben eine hookDisplayHeader-Methode. Jedes einzelne fügt zwischen einer und vier CSS- und JavaScript-Dateien hinzu. Das bedeutet, dass jede einzelne Seite Ihres Shops 15 bis 60 zusätzliche HTTP-Anfragen allein durch Module lädt, unabhängig davon, ob diese Module auf der aktuellen Seite irgendetwas anzeigen.
Betrachten Sie ein konkretes Beispiel. Ein Modul, das ein Größenratgeber-Popup auf Produktseiten hinzufügt, benötigt eine CSS-Datei für die Popup-Gestaltung und eine JavaScript-Datei für das Popup-Verhalten. Wenn der Entwickler diese Assets in hookDisplayHeader ohne eine bedingte Prüfung registriert, laden diese beiden Dateien auf der Startseite, auf jeder Kategorieseite, auf der Warenkorbseite, auf der Checkout-Seite und auf jeder CMS-Seite. Der Größenratgeber wird auf keiner dieser Seiten jemals erscheinen, aber der Browser lädt, analysiert und verarbeitet dennoch die CSS- und JavaScript-Dateien.
Multiplizieren Sie dies mit jedem Modul, das dem gleichen Muster folgt, und Sie beginnen zu verstehen, warum manche PrestaShop-Shops 2 MB oder mehr an Modul-Assets auf Seiten laden, auf denen die meisten dieser Assets völlig unnötig sind.
Der actionFrontControllerSetMedia-Hook
PrestaShop 1.7 führte den actionFrontControllerSetMedia-Hook als modernere Alternative zu displayHeader für die Asset-Registrierung ein. Dieser Hook wurde speziell für die Registrierung von CSS- und JavaScript-Dateien mit den neuen Methoden registerStylesheet und registerJavascript entwickelt. Diese Methoden bieten Vorteile gegenüber den älteren addCSS- und addJS-Funktionen, einschließlich der Möglichkeit, Ladepriorität festzulegen, async- oder defer-Attribute zu spezifizieren und die Position (Head oder Bottom) von Script-Tags zu steuern.
Jedoch leidet actionFrontControllerSetMedia genau unter dem gleichen grundlegenden Problem wie displayHeader: Er wird auf jeder Seite ausgelöst. Ein Modul, das Assets in hookActionFrontControllerSetMedia registriert, ohne den aktuellen Controller zu prüfen, lädt diese Assets überall, genau wie beim älteren Ansatz.
Der einzige Unterschied ist die Registrierungsmethode, nicht der Ladungsumfang. Ob ein Entwickler addCSS in displayHeader oder registerStylesheet in actionFrontControllerSetMedia verwendet — das Ergebnis ist dasselbe, wenn keine bedingte Logik hinzugefügt wird. Die Assets laden global.
Korrektes bedingtes Laden: Was gute Module tun
Ein gut entwickeltes Modul prüft, welche Seite der Kunde betrachtet, bevor es irgendwelche Assets lädt. Der Standardweg dafür in PrestaShop ist die Prüfung des Controller-Namens. Jede Front-Office-Seite wird von einem bestimmten Controller bereitgestellt: IndexController für die Startseite, CategoryController für Kategorieseiten, ProductController für Produktseiten, CartController für den Warenkorb und so weiter.
Innerhalb der hookDisplayHeader- oder hookActionFrontControllerSetMedia-Methode kann der Entwickler auf den aktuellen Controller über $this->context->controller zugreifen. Durch Prüfung des Klassennamens oder der php_self-Eigenschaft des Controllers kann das Modul entscheiden, ob es seine Assets laden soll. Ein Produktbewertungsmodul beispielsweise sollte prüfen, ob die aktuelle Seite eine Produktseite ist, und nur in diesem Fall seine CSS- und JavaScript-Dateien laden.
Dieser bedingte Ansatz ist nicht schwer umzusetzen. Er fügt der Hook-Methode vielleicht fünf bis zehn Zeilen Code hinzu. Dennoch überspringt eine überraschend große Anzahl von Modulen, einschließlich populärer kostenpflichtiger Module auf dem PrestaShop Addons-Marktplatz und bekannter kostenloser Module, diese Prüfung vollständig. Der Grund ist eine Kombination aus Entwickler-Bequemlichkeit und der Tatsache, dass PrestaShop bedingtes Laden in seiner Modul-Entwicklungsdokumentation weder erzwingt noch aktiv fördert.
Manche Entwickler argumentieren, dass globales Laden von Assets die Kompatibilität mit benutzerdefinierten Themes oder ungewöhnlichen Seitenkonfigurationen sicherstellt, bei denen der Modulinhalt unerwartet erscheinen könnte. Obwohl dieses Argument einen Funken Wahrheit enthält, rechtfertigt es nicht die Leistungskosten. Ein besserer Ansatz ist es, Assets standardmäßig bedingt zu laden und eine Konfigurationsoption bereitzustellen, um globales Laden für Shops zu aktivieren, die es benötigen.
Die setMedia-Methode: Best Practices für Modulentwickler
Für Modulentwickler, die diesen Artikel lesen, hier die Best Practices für Asset-Laden, die die Website-Leistung Ihrer Nutzer respektieren.
Erstens: Verwenden Sie in PrestaShop 1.7 und später immer den actionFrontControllerSetMedia-Hook statt displayHeader für die Asset-Registrierung. Der neuere Hook bietet bessere Kontrolle über das Asset-Ladeverhalten und hält Ihre Asset-Registrierung von der HTML-Ausgabe getrennt.
Zweitens: Prüfen Sie immer den aktuellen Controller, bevor Sie Assets registrieren. Verwenden Sie einen Whitelist-Ansatz: Definieren Sie die Liste der Controller, auf denen Ihr Modul Inhalte anzeigt, und registrieren Sie Assets nur, wenn der aktuelle Controller auf dieser Liste steht. Dies ist wartbarer als ein Blacklist-Ansatz, da neue Seitentypen, die in zukünftigen PrestaShop-Versionen hinzugefügt werden, Ihre Assets nicht versehentlich laden.
Drittens: Verwenden Sie registerStylesheet und registerJavascript anstelle von addCSS und addJS. Die neueren Methoden ermöglichen die Angabe einer ID für jedes Asset, wodurch es Themes und anderen Modulen möglich wird, Ihre Assets sauber zu überschreiben oder zu entfernen. Sie unterstützen auch Prioritätseinstellungen, die die Reihenfolge steuern, in der Assets geladen werden.
Viertens: Überlegen Sie, ob Ihr JavaScript wirklich im Head geladen werden muss oder ob es am Ende der Seite mit dem defer-Attribut geladen werden kann. JavaScript im Head blockiert das Rendering, was bedeutet, dass der Browser keinen Inhalt anzeigen kann, bis Ihr Code vollständig heruntergeladen und analysiert ist. Das Verschieben von Codes an das Seitenende oder das Hinzufügen von defer eliminiert dieses render-blockierende Verhalten.
Fünftens: Minimieren Sie Ihre Asset-Dateigrößen. Minifizieren Sie Ihre CSS- und JavaScript-Dateien vor der Verteilung Ihres Moduls. Entfernen Sie ungenutzte CSS-Regeln. Vermeiden Sie es, ganze Bibliotheken wie jQuery UI oder Bootstrap zu bündeln, wenn Sie nur einen kleinen Teil ihrer Funktionalität nutzen. Jedes Kilobyte zählt, wenn die Assets Ihres Moduls bei Tausenden von Seitenaufrufen pro Tag geladen werden.
Die Leistungsauswirkung globaler Assets messen
Um zu verstehen, wie viel globales Asset-Laden Ihren Shop kostet, benötigen Sie konkrete Messungen. Öffnen Sie Chrome DevTools auf Ihrer Startseite und gehen Sie zum Netzwerk-Tab. Laden Sie die Seite neu und filtern Sie Anfragen nach dem Pfad /modules/. Zählen Sie die Gesamtzahl der Anfragen und ihre kombinierte Größe. Das ist Ihr Modul-Asset-Overhead auf einer Seite, auf der die meisten Module keinen Inhalt anzeigen.
Machen Sie nun dasselbe auf einer Produktseite, wo viele Module ihre Assets legitimerweise benötigen. Vergleichen Sie die Zahlen. In einem gut optimierten Shop sollte die Produktseite deutlich mehr Modul-Assets laden als die Startseite, denn dort zeigen die meisten Module ihre Inhalte an. In einem schlecht optimierten Shop sind die Zahlen nahezu identisch, weil jedes Modul alles überall lädt.
Ein konkreter Benchmark aus realen Audits: Ein Shop mit 35 installierten Modulen lud 1,8 MB an Modul-CSS und -JavaScript auf der Startseite. Nach der Implementierung von bedingtem Laden bei allen Modulen sanken die Startseiten-Modul-Assets auf 340 KB. Die Produktseite, auf der die meisten Module ihre Assets legitimerweise benötigten, ging von 2,1 MB auf 1,4 MB zurück. Die Ladezeit der Startseite verbesserte sich um 1,3 Sekunden und der Lighthouse-Performance-Score stieg von 42 auf 71.
Diese Zahlen sind keine Ausnahme. Globales Asset-Laden ist eine der größten einzelnen Quellen für unnötiges Seitengewicht in PrestaShop-Shops, und die Behebung führt oft zu den dramatischsten Leistungsverbesserungen.
Wie Sie problematische Module in Ihrem Shop identifizieren
Das Auffinden von Modulen, die Assets global laden, erfordert einen systematischen Ansatz. Beginnen Sie mit dem Netzwerk-Tab in den DevTools wie oben beschrieben. Fragen Sie sich für jedes Modul-Asset, das auf der Startseite lädt: Zeigt dieses Modul irgendeinen Inhalt auf der Startseite an? Wenn die Antwort Nein lautet, lädt dieses Modul Assets unnötigerweise.
Ein weiterer Ansatz ist die Verwendung des PrestaShop-Debug-Profiling-Modus. Wenn das Profiling aktiviert ist (durch Setzen von _PS_DEBUG_PROFILING_ auf true in Ihrer config/defines.inc.php), zeigt PrestaShop detaillierte Hook-Ausführungsdaten am unteren Rand jeder Seite an. Diese Daten zeigen Ihnen genau, welche Module auf jedem Hook ausgeführt werden und wie lange sie brauchen. Jedes Modul, das auf displayHeader oder actionFrontControllerSetMedia ausgeführt wird, aber auf keinem Display-Hook sichtbare Ausgabe auf der aktuellen Seite produziert, ist ein Kandidat für Optimierung.
Sie können auch programmatisch prüfen, indem Sie den Quellcode jedes Moduls untersuchen. Schauen Sie sich die hookDisplayHeader- und hookActionFrontControllerSetMedia-Methoden in der Haupt-PHP-Datei des Moduls an. Wenn die Methode addCSS-, addJS-, registerStylesheet- oder registerJavascript-Aufrufe ohne bedingte Prüfungen des Controller-Namens enthält, lädt dieses Modul Assets global.
Für Shops mit vielen Modulen kann diese manuelle Überprüfung zeitaufwändig sein. Eine praktische Abkürzung ist, sich zuerst auf die größten Assets zu konzentrieren. Sortieren Sie die Modul-Assets im Netzwerk-Tab nach Größe und beginnen Sie mit der Untersuchung der größten Dateien. Eine 200-KB-JavaScript-Datei, die global lädt, ist ein viel größeres Problem als eine 3-KB-CSS-Datei, also priorisieren Sie entsprechend.
Fixes von Modulentwicklern anfordern
Wenn Sie ein Modul identifizieren, das Assets global lädt, sollte Ihr erster Schritt sein, den Entwickler zu kontaktieren und eine Behebung anzufordern. Die meisten professionellen Modulentwickler sind empfänglich für Anfragen zur Leistungsverbesserung, besonders wenn Sie spezifische Daten über die Auswirkungen liefern können.
Schreiben Sie eine klare, technische Nachricht, die das Problem erklärt. Erwähnen Sie, dass die hookDisplayHeader-Methode des Moduls CSS und JavaScript auf jeder Seite lädt, ohne den aktuellen Controller zu prüfen. Geben Sie an, welche Seiten die Assets tatsächlich benötigen und welche Seiten sie unnötigerweise laden. Nennen Sie die Dateigrößen und die geschätzte Leistungsauswirkung. Wenn Sie Lighthouse-Scores haben, die den Vorher-Nachher-Vergleich zeigen, wenn die Assets des Moduls blockiert werden, fügen Sie diese bei.
Wenn der Entwickler reagiert, wird er typischerweise innerhalb weniger Wochen ein Update veröffentlichen, das bedingtes Laden hinzufügt. Wenn der Entwickler nicht reagiert oder das Anliegen abtut, haben Sie mehrere Optionen: das bedingte Laden selbst implementieren, indem Sie den Modul-Code bearbeiten, ein Performance-Modul verwenden, das Assets auf bestimmten Seiten selektiv blockieren kann, oder ein alternatives Modul finden, das besseren Entwicklungspraktiken folgt.
Workarounds, wenn Sie das Modul nicht ändern können
Manchmal können Sie ein Modul nicht direkt ändern. Es könnte mit IonCube verschlüsselt sein, es könnte ein Modul sein, von dem Sie keinen Fork pflegen möchten, oder das Modul könnte Ihre Änderungen bei jedem Update überschreiben. In diesen Fällen benötigen Sie Workarounds.
Ein effektiver Workaround besteht darin, ein kleines benutzerdefiniertes Modul zu erstellen, das das problematische Modul von displayHeader und actionFrontControllerSetMedia abhängt und dann die Assets nur auf den Seiten wieder hinzufügt, wo sie benötigt werden. Dieses benutzerdefinierte Modul würde den actionFrontControllerSetMedia-Hook mit hoher Priorität verwenden (damit es nach dem problematischen Modul ausgeführt wird) und Media::unregisterStylesheet sowie Media::unregisterJavascript aufrufen, um die global geladenen Assets zu entfernen. Dann, auf Seiten, wo die Assets tatsächlich benötigt werden, würde es sie erneut registrieren.
Ein weiterer Workaround ist die Verwendung der Theme.yml-Datei, um Modul-Assets zu überschreiben. In PrestaShop 1.7 und später kann die theme.yml-Konfigurationsdatei des Themes bestimmte Modul-Assets vom Laden ausschließen. Dies ist eine Lösung auf Theme-Ebene, die über Modul-Updates hinweg bestehen bleibt. Sie entfernt die Assets jedoch von allen Seiten und nicht selektiv, sodass Sie dies mit dem erneuten Hinzufügen der Assets auf bestimmten Seiten über ein benutzerdefiniertes Modul oder Theme-Template kombinieren müssten.
Eine dritte Option, verfügbar in PrestaShop 8.x, ist die Nutzung der eingebauten Prioritäts- und Entfernungsfunktionen des Asset-Management-Systems. PrestaShop 8 hat die Asset-Pipeline verbessert, um Themes mehr Kontrolle darüber zu geben, welche Modul-Assets geladen werden. Prüfen Sie die Dokumentation Ihres Themes für spezifische Anweisungen, wie Sie diese Funktionen nutzen können.
Unabhängig davon, welchen Ansatz Sie wählen: Testen Sie nach Änderungen immer gründlich. Das Entfernen des CSS eines Moduls kann sein visuelles Erscheinungsbild beeinträchtigen, und das Entfernen seines JavaScripts kann interaktive Funktionen zerstören. Testen Sie jeden Seitentyp, auf dem das Modul weiterhin korrekt funktionieren sollte.
Prävention: Module vor der Installation bewerten
Der beste Weg, Probleme mit globalem Asset-Laden zu vermeiden, ist die Bewertung von Modulen vor der Installation. Obwohl dies nicht immer möglich ist, gibt es Prüfungen, die Sie durchführen können.
Wenn das Modul einen Demo-Shop bereitstellt, besuchen Sie ihn und untersuchen Sie die Startseite mit den DevTools. Prüfen Sie, ob die Assets des Moduls auf Seiten geladen werden, auf denen das Modul keinen Inhalt anzeigt. Wenn Assets in der Demo global laden, werden sie auch in Ihrem Shop global laden.
Wenn Sie vor dem Kauf Zugang zum Quellcode des Moduls haben (manche Marktplätze bieten Code-Vorschauen an), schauen Sie sich die hookDisplayHeader- und hookActionFrontControllerSetMedia-Methoden an. Prüfen Sie auf bedingte Ladelogik. Das Fehlen jeglicher Controller-Prüfung ist ein Warnsignal.
Lesen Sie die Bewertungen und das Support-Forum des Moduls. Leistungsbewusste Nutzer melden globale Asset-Ladeprobleme häufig in Bewertungen. Wenn mehrere Nutzer sich darüber beschwert haben, dass das Modul ihren Shop verlangsamt, nehmen Sie dieses Feedback ernst.
Berücksichtigen Sie schließlich die allgemeine Code-Qualität des Entwicklers. Entwickler, die in einem Bereich Best Practices folgen, tun dies tendenziell auch in anderen. Wenn der Code eines Moduls sauber, gut dokumentiert und den PrestaShop-Coding-Standards entsprechend ist, ist es wahrscheinlicher, dass er auch das Asset-Laden korrekt handhabt. Wenn der Code unordentlich und schlecht strukturiert ist, ist globales Asset-Laden wahrscheinlich nur eines von vielen Problemen.
War diese Antwort hilfreich?
Haben Sie noch Fragen?
Can't find what you're looking for? Send us your question and we'll get back to you quickly.