Gleich als Facebook es 2015 auf den Markt brachte, erkannten Frontend-Entwickler, welches Potenzial React bietet. Hohe Leistungsfähigkeit, agile Benutzerschnittstellen – für Kenner zählt React definitiv zu den besten JavaScript-Frameworks.
Grund genug, diese Bibliothek in verschiedene unserer Adobe Website-Projekte zu implementieren. React bietet die Möglichkeit, Websites oder mobile Apps mit hochinteraktiven Elementen auszustatten. Allein mit den Kernkomponenten des Adobe Experience Manager (AEM) gelingt das nämlich nicht so ohne Weiteres. Gemeint sind hier Elemente wie interaktive Infografiken, ein User-Portal mit Dashboards und Widgets oder eine Live-Simulation im Investment-Bereich.
React in Kombination mit AEM optimiert das Benutzererlebnis und bietet Vorteile bei der Projektentwicklung. Regelmäßig sehen sich Webentwickler dabei allerdings vor Herausforderungen gestellt.
Unsere React-Komponenten gelangen gemeinhin über eine große Bootstrap-Datei auf die AEM-Website. Das funktioniert, wenn unser DOM (Document Object Model) HTML-Elemente eines bestimmten Datentyps (hier data-type=”react”) enthält, den wir in AEM festlegen können. Ist ein solches Element, oder auch mehrere, vorhanden, prüfen wir das „data-id“-Attribut und übersetzen (rendern) die relevanten Komponenten in besagte HTML-Elemente.
Eine solche große Bootstrap-Datei bedeutet, dass jede verfügbare React-Komponente darin Eingang findet. Der kompilierte Code ist dann zwangsläufig sehr umfangreich. Wenn Sie aber zum Beispiel drei Webseiten mit jeweils eigener React-Komponente haben, ist es wenig sinnvoll, den Code dieser Seiten mit Bausteinen zu belasten, die an dieser Stelle gar nicht relevant sind.
Idealerweise würde jede Seite einzeln geprüft und es würden jeweils nur diejenigen Code-Bestandteile angefragt, die auf der aktuellen Seite überhaupt gebraucht werden. Und damit sind wir beim Thema „dynamisches Code Chunking“ mit WebPack! Durch die Implementierung von WebPack ist es uns gelungen, den React-Code in AEM aufzusplitten. Und zwar so:
Zunächst einmal muss man wissen, dass sich dynamische React-Chunks und reguläre JavaScript-Importe ausschließen. Der nachfolgende Screenshot zeigt, wie diese Importe aussehen:
CODE
import { Component1 } from './app/components/component1.component';
import { Component2 } from './app/components/component2.component';
import { Multiple, Components } from './app/components/multiplecomponents.component';
import * as alias from 'we-need-everything-from-this-file.json';
import { Service } from './app/services/service.service';
Sind diese Importe Bestandteil unserer Bootstrap-Datei und kompilieren wir dann den Code, wird jede Komponente in die Zieldatei importiert. Daher steigen wir um: auf die Methode des dynamischen Imports(). Diese Methode basiert auf so genannten „asynchronous promises“. Der neue Code könnte dann zum Beispiel so aussehen:
Dynamischer JavaScript-Import()
CODE
import('./app/components/component1.component').then((Component1File) => {
// Do stuff with your import!
console.log(Component1File.Component1);
});
import ('./app/components/multiplecomponents.component').then((MultipleComponentsFile) => {
console.log(MultipleComponentsFile.Multiple);
console.log(MultipleComponentsFile.Components);
});
// and so on...
WebPack generiert dann für jede importierte Komponente separate Code-Chunks, die im Hintergrund dynamisch abgefragt werden. Über die Kommentarfunktion von WebPack lassen sich die Chunks auch im Voraus benennen. Zum Beispiel so:
CODE
import(/* webpackChunkName: "core" */'./app/components/component1.component').then((Component1File) => {
// Do stuff with your import!
console.log(Component1File.Component1);
});
Der vorstehende Programmcode fordert WebPack auf, einen Chunk namens ‚core’ zu erstellen. Jedes Mal, wenn Component1 ins Spiel kommt, wird der Chunk ‚core’ über das Netzwerk angefragt. Derselbe Chunk-Name kann auch an mehrere unterschiedliche Importe vergeben werden: Diese Importe gelangen dadurch alle in denselben Chunk.
Das ist in Bezug auf JavaScript schon alles. Gar nicht so schwierig, oder? Jetzt muss noch WebPack konfiguriert werden.
Bei AEM-Projekten kommt bei uns für das Frontend mehrheitlich Grunt zum Einsatz. Damit das Chunking funktioniert, muss in der Grunt-Datei die WebPack-Konfiguration entsprechend angepasst werden. Die Konfiguration für unseren Input bleibt unverändert:
CODE
entry: {
'main': ['./src/app.bootstrap.tsx'],
'subsite1': './src/assets/sass/subsite1.scss',
'subsite2': './src/assets/sass/subsite2.scss',
'subsite3': './src/assets/sass/subsite3.scss',
'addon-author': './src/assets/sass/addon-author.scss',
'labels': ['./src/assets/js/labels.ts'],
},
Ausgehend von diesen Dateien erzeugt WebPack dann Bundles. In app.bootstrap.tsx findet es unsere dynamischen Importe von oben. Es weiß dann, dass daraus jeweils einzelne Chunks zu erstellen sind. Jetzt legen wir die Output-Konfiguration fest, d. h. unser Build-Verzeichnis, die Namen der Bundles und Chunks und der Weg, über den unsere Chunks angefordert werden. Das geht wie folgt:
CODE
output: {
path: path.resolve(__dirname, 'build'),
filename: 'js/[name].bundle.js',
chunkFilename: path_to_our_aem_clientlibs/chunks/[name].[chunkhash].chunk.js',
publicPath: "/",
},
Im Beispiel oben erkennt man, dass unser Code im Build-Verzeichnis vorliegt. Die Bundles befinden sich im JS-Verzeichnis und die Chunks in einem Verzeichnis, das genauso benannt ist wie unsere clientseitigen Bibliotheken in AEM (auch ClientLibs genannt). Warum? Wenn der Build-Vorgang abgeschlossen ist, sollen alle Dateien in die eigentliche AEM-Struktur kopiert werden. Auf diese Weise können wir sie inklusive Verzeichnis kopieren. WebPack holt sich die Chunks über den Root-Pfad.
Achtung: Wir verwenden [chunkhash]. An die Chunk-Dateinamen wird dadurch automatisch ein Rautenzeichen angehängt. Der Grund hierfür liegt im Caching. Wenn der Code im Chunk geändert wird, ändert sich auch der Hashcode. Der Browser weiß dann, dass er den Chunk neu laden muss, und verwendet nicht den im Cache vorhandenen Chunk. Anderenfalls bliebe der Chunk-Name auch im Fall einer Änderung unverändert. Website-Besuchern würde dann eine alte Version des Chunks angezeigt.
Das war es auch schon fast! Im letzten Schritt kopieren wir die Chunk-Dateien an die richtige Stelle in der AEM-Struktur. Dazu nutzen wir den „Copy“-Task aus Grunt. Die Konfiguration sieht dann so aus:
CODE
{
cwd: "./build/",
dest: aem_design_etc_overrides + '/designs/project/clientlibs/chunks/',
expand: true,
flatten: true,
src: "etc/designs/project/clientlibs/chunks/*.js"
},
Wir nehmen die Chunks aus dem Build-Verzeichnis (Sie erinnern sich: WebPack erhielt die Order, den Code dort zu generieren) und kopieren sie in das ClientLibs-Verzeichnis in AEM, in den angegebenen Chunks-Ordner.
Das war alles. Mit diesen Änderungen ist es uns gelungen, den React-Code in einzelne Chunks aufzusplitten. AEM fordert diese nun dynamisch über das Netzwerk an. Klingt doch gut, oder? Aber welches konkrete Ergebnis dürfen Sie erwarten? Verbessert sich die Leistung Ihrer Website auch tatsächlich?
Absolut! Wir haben einige „Vorher/Nachher“-Tests durchgeführt. Dabei haben wir einige Seiten hundert Male aufgerufen. Sehen Sie sich den Unterschied an!
Vor der Erstellung der Code-Chunks in React:
Seite |
Durchschnittliche Ladezeit (Millisekunden) |
Kürzeste Ladezeit (Millisekunden) |
Längste Ladezeit (Millisekunden) |
Index |
3948 |
1165 |
6996 |
Übersicht Seite 1 |
1521 |
246 |
5595 |
Übersicht Seite 2 |
37895 |
20713 |
55070 |
Detail Seite 1 |
13201 |
158 |
31618 |
Detail Seite 2 |
1573 |
45 |
6731 |
Insgesamt |
11628 |
45 |
55070 |
Nach der Erstellung der Code-Chunks in React:
Seite |
Durchschnittliche Ladezeit (Millisekunden) |
Kürzeste Ladezeit (Millisekunden) |
Längste Ladezeit (Millisekunden) |
Inhalt |
3840 |
1160 |
6281 |
Übersicht Seite 1 |
810 |
116 |
5457 |
Übersicht Seite 2 |
35250 |
20642 |
53209 |
Detail Seite 1 |
12944 |
110 |
30390 |
Detail Seite 2 |
1052 |
40 |
5252 |
Insgesamt |
9454 (-19 %) |
40 (-12 %) |
53209 (-3 %) |
Die Zahlen sprechen eine klare Sprache: Es ist uns gelungen, die Ladezeit der Seiten im Schnitt um 19 % zu verkürzen. Dabei muss man wissen, dass wir bei dieser speziellen Website überhaupt nur für die Code-Hauptblöcke Chunks erstellt haben. Es besteht also sogar noch weiteres Optimierungspotenzial.
Nun sind Sie an der Reihe! Probieren Sie es aus: Sie werden sehen, wie sich die Performance Ihrer Website verbessert.