Shopware 6.6 wurde vor Kurzem released und bringt zahlreiche Verbesserungen unter der Haube mit. Es handelt sich um ein sehr technisches Release, welches einerseits alte Zöpfe abschneidet, dadurch aber neue Möglichkeiten bietet. Ein Feature davon ist gleichermaßen sehr interessant und wurde dadurch von der Entwickler-Community sehr stark diskutiert. Eure JavaScript-Plugins können ab sofort nach Bedarf geladen werden.

Früher (und mit früher meine ich, dass dies schon in Shopware 5 der Fall war) wurden sämtliche JavaScript-Plugins, die Shopware (aber auch die installierten Erweiterungen) mitbringen, zu einem Bundle komprimiert, welches dann über sämtliche Seiten des Shops geladen werden. Dies birgt aus der Performance-Perspektive gleich mehrere Probleme:

  • Das Bundle kann mitunter schon sehr groß werden, ein leeres Shopware 6.5 bringt bereits knapp 600 KB mit. Vollausgestattet mit Erweiterungen kommen gerne 1-2 MB zusammen.

  • Nicht jedes JavaScript-Plugin muss auch auf jeder Seite verfügbar sein.

  • Es gibt keine Unterscheidung zwischen mobil oder Desktop.

Der neue Ansatz bietet gleich mehrere Vorteile: Zum einen werden JavaScript-Plugins nur noch dann geladen, wenn das DOM danach verlangt

Der zweite Vorteil ist, dass die Plugin-Daten asynchron nachgeladen werden. Sie blockieren daher nicht den Aufbau der Website. Inzwischen gehört dies auch zu einer wichtigen Messgröße bei Performance-Messtools wie Google Lighthouse.

Sidenote: Der Ansatz sorgt für einen minimalen Overhead, würde man die reine Dateigröße aller asynchronen Requests mit der des Bundles vergleichen. Allerdings überwiegen die gewonnenen Vorteile enorm.

Registrieren eines JavaScript-Plugins

Shopware verfügt über einen so genannten PluginManager, über den eure Plugins registriert werden können. Damit ihr die Vorteile aus dem neuen Registrierungsansatz nutzen könnt, solltet ihr es mit einem Element aus eurem DOM verbinden. Dies markiert gleichzeitig den Haupt-Wirkungsbereich eures Plugins. Typischerweise wird dies in Shopware mittels data-Attributen umgesetzt. Eurer Template hat dann ein dediziertes Attribut an einem HTML-Tag eurer Wahl - beispielsweise

.

Tipp: Es funktionieren zwar sämtliche Selektoren, das bedeutet ihr seit frei bei der Wahl eures Mounting-Points. Es empfiehlt sich dennoch, den Coding-Conventions von Shopware 6 zu folgen.

Die vollständige Template-Datei in unserem Beispiel sähe folgendermaßen aus:

[pluginroot/src/Ressources/view/storefront/page/content/index.html.twig]

{% sw_extends '@Storefront/storefront/page/content/index.html.twig' %}

{% block base_main_inner %}
    {{ parent() }}

    <div data-load-me-if-needed-only="true"></div>
{% endblock %}

Habt ihr dies vorbereitet, wird in der main.js (nach wie vor ist dies euer Einstiegspunkt um euer Plugin zu schreiben) das Plugin registriert:

[pluginroot/src/Ressources/app/storefront/src/main.js]

window.PluginManager.register('AsyncTest', () => import('./plugin/async-test.plugin'), '[data-load-me-if-needed-only="true"]');

In diesem Aufruf verstecken sich die beiden Komponenten, die miteinander verbunden werden: Der Selektor '[data-load-me-if-needed-only="true"]', welcher dem Attribut im Template entspricht, sowie die Pfadangabe './plugin/async-test.plugin', welche eure eigentliche Plugin-Logik enthält. Diese Logik wird in eurer Storefront asynchron, nach Bedarf geladen.

[pluginroot/src/Ressources/app/storefront/src/plugin/async-test.plugin.js]

import Plugin from 'src/plugin-system/plugin.class';

export default class AsyncTestPlugin extends Plugin {
    init() {
        // eure Plugin-Logik sowie weitere Methoden
    }
}

Die Performance-Vorteile

Im ausgelieferten Zustand besteht euer JavaScript-Plugin aus mehreren Dateien: Einmal dem Einstiegspunkt, welche inhaltlich eurer main.js entspricht. Diese sollte nicht mehr als Plugin-Registrierungen oder-Overrides enthalten. Dann bekommt jedes eurer JavaScript-Plugins eine weitere Datei, sofern ihr die Plugins wie in der oben beschriebenen Weise registriert. Diese Dateien werden erst geladen, wenn das DOM es benötigt. In diesem kleinen Beispiel sind es zwar nur wenige KB, die eingespart werden, aber in der Regel beinhalten eure Erweiterungen deutlich mehr Komplexität. Zudem summiert es sich, je nach Anzahl der Individualisierungsgrad eures Shops.

In eurer Storefront werdet ihr nur diesen Aufruf zu eurem Einstiegspunkt finden:

<script type="text/javascript" src="http://mystore.url/theme/140d3e1034b4bae36039a6a20638664b/js/js-async-test/js-async-test.js?1712670563" defer></script>

Wichtig ist dabei das Attribut 'defer', was dem Browser mitteilt, dass das Script-File parallel geladen und später (nach dem Parsen des Dokuments) ausgeführt werden soll. Diese Funktionalität ist nativ auf allen gängigen Browsern vorhanden.

Fazit:

Diese Umstellung wurde lange erwartet und löst viele Performance-Probleme eurer Storefront. Es ist nicht viel Arbeit, eure JavaScript-Plugins umzustellen, gleichzeitig wird eurer Shop spürbar schneller. Das reine Feature-Set der Version 6.6 ist äußerlich unscheinbar, aber unter der Haube hat sich eine Menge getan, wodurch sich das Update definitiv auszahlt!