Der Acolad-Blog

React-Code in AEM: Arbeiten mit dynamischen Chunks

Geschrieben von Bertijn Pauwels | 06.02.2020 08:00:00

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.

Die Herausforderung: JavaScript-Code mit React in AEM reduzieren


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:


Schritt 1: Code-Splitting über dynamische Importe in JavaScript

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:

JavaScript-Standardimporte

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:

Benennung von Code-Chunks in React über „Magic Comments“ in WebPack

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.


Schritt 2: Konfiguration von WebPack


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:

Input-Konfiguration in WebPack

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:


Output-Konfiguration in WebPack

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:



WebPack-Konfiguration für den Kopiervorgang

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 Ergebnis: eine im Durchschnitt um 19 % verbesserte Leistung der Website

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.

Web-Performance: der schnelle Weg zum besseren Benutzererlebnis