Depuis son lancement par Facebook en 2015, React a conquis de nombreux développeurs front-end. Pour la création d'interfaces utilisateur performantes et agiles, il a même été élu parmi les meilleurs frameworks JavaScript existants.
C'est pourquoi nous avons commencé à mettre en œuvre React dans certains de nos projets de sites web Adobe. Pour un site web ou une appli mobile, React permet de concevoir des éléments hautement interactifs, qui ne sont pas faciles à créer à partir des composants de base d'Adobe Experience Manager (AEM). Imaginez une infographie interactive, un portail utilisateur avec des tableaux de bord et des widgets, ou encore des simulations d'investissement en temps réel.
Le problème : comment réduire le code JavaScript avec React dans AEM ?
Même si React, associé à AEM, offre des avantages pour l'expérience utilisateur et le développement de projets, de nombreux projets se heurtent à un problème courant.
Nous chargeons nos composants React dans le site web AEM grâce à un gros fichier bootstrap. Celui-ci vérifie que notre modèle (Document Object Model - DOM) contient des éléments HTML avec l'attribut « data-type="react" », que nous pouvons définir dans AEM. Si React trouve un ou plusieurs de ces éléments, nous vérifions l'attribut data-id et nous modifions en conséquence les composants qu'il nous faut dans ces éléments HTML.
Avec un fichier de bootstrap aussi gros, il faut intégrer tous les composants React existants, ce qui produit une grande quantité de code. Par exemple, si vous avez trois pages web, chacune avec son propre composant React, mais quel est l'intérêt de surcharger le code avec des composants qui ne serviront pas à grand-chose.
L'idéal serait de vérifier chaque page et de limiter les requêtes au code des composants utiles pour chacune de ces pages. C'est là qu'intervient la segmentation dynamique du code avec WebPack. Voilà comment nous avons réussi à implémenter WebPack pour segmenter le code React dans AEM.
1ère étape : Segmentation du code par des imports dynamiques dans JavaScript
La première étape de la création de segments dynamiques de code React consiste à se débarrasser des imports JavaScript habituels, comme ce qui est montré ci-dessous :
Imports JavaScript standard
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';
Lorsque nous mettons ces imports dans notre fichier de bootstrap et que compilons le code, chaque composant est importé dans le fichier généré. À partir de maintenant, nous utiliserons la méthode « dynamic import()», qui utilise des promesses asynchrones. Le code mis à jour devrait donc ressembler à ceci :
Méthode dynamique import() de JavaScript
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 va ensuite générer des segments de code distincts pour chaque composant importé. Ces segments seront interrogés de manière dynamique en masqué. Vous pouvez aussi utiliser la fonction WebPack « magic comments », qui vous permet de nommer vos segments en avance de phase. Un exemple :
« Magic comments » WebPack pour la désignation des segments de code React
CODE
import(/* webpackChunkName: "core" */'./app/components/component1.component').then((Component1File) => {
// Do stuff with your import!
console.log(Component1File.Component1);
});
Le code ci-dessus ordonne à WebPack de créer un segment nommé « core » . Chaque fois que Component1 est inclus, le segment « core » est demandé sur le réseau. Ce nom de segment peut aussi être attribué à plusieurs imports différents, qui seront donc inclus dans le même segment.
Voilà pour la partie JavaScript. C'est plutôt simple, non ? Il nous reste plus qu'à configurer notre WebPack.
2e étape : la configuration de WebPack
Dans la plupart de nos projets AEM, nous utilisons Grunt pour notre front-end. Pour que la segmentation fonctionne, il faut modifier la configuration de WebPack dans notre fichier Grunt. La configuration de nos fichiers d'entrée reste inchangée :
Configuration des données d'entrée dans 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'],
},
WebPack créera des bundles à partir de ces fichiers. Dans app.bootstrap.tsx, il rencontrera nos imports dynamiques ci-dessus et saura comment créer des segments distincts pour chacun. Il faut maintenant définir la configuration des sorties, c'est-à-dire le répertoire de destination, les noms des bundles et des segments, ainsi que le chemin d'accès à nos segments. Voilà comment procéder :
Configuration des sorties dans 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: "/",
},
Dans l'exemple ci-dessus, vous pouvez voir que nous construisons notre code dans le répertoire « build », nous mettons les bundles dans le répertoire « js » et les segments dans un répertoire qui porte le même nom que nos bibliothèques client AEM (ClientLibs). Pourquoi ? Parce qu'à la fin du processus de construction, il faut copier tous les fichiers dans la structure AEM réelle et, en procédant ainsi, on peut les copier avec le répertoire. Le chemin d'accès indiqué à WebPack pour trouver nos segments est la racine.
Notez bien que nous utilisons la fonction [chunkhash], qui ajoute automatiquement un dièse au nom de notre fichier de segment, ceci à des fins de mise en cache. Lorsque le code du segment change, le code dièse change aussi. Ainsi, le navigateur saura qu'il doit de nouveau aller chercher le segment au lieu d'utiliser celui qui était dans la mémoire cache. Si nous ne le faisions pas, le segment garderait le même nom, même après avoir été modifié et les visiteurs obtiendraient une ancienne version du segment.
Bon, on a presque fini ! Il ne reste plus qu'à copier les fichiers de segments au bon endroit dans la structure AEM. Pour cela, on utilise la tâche « copy » depuis Grunt. Voici à quoi ressemble la configuration :
Configuration WebPack
CODE
{
cwd: "./build/",
dest: aem_design_etc_overrides + '/designs/project/clientlibs/chunks/',
expand: true,
flatten: true,
src: "etc/designs/project/clientlibs/chunks/*.js"
},
Les segments proviennent du répertoire build (pour rappel, nous venons de demander à WebPack d'y construire le code) et nous les copions dans le répertoire ClientLibs dans AEM, dans le dossier de segments spécifié.
Le résultat : un site web dont la performance a été améliorée de 19 % en moyenne
Et c'est tout. En procédant à ces simples changements, nous avons réussi à fragmenter notre code React en plusieurs segments distincts. AEM va les chercher sur le réseau de manière dynamique. Plutôt intéressant, non ? Mais concrètement, qu'est-ce que ça donne ? Avons-nous amélioré la performance de notre site web ? Oui ! Nous avons fait quelques tests « avant/après », en navigant sur des pages une centaine de fois, et regardez la différence !
Avant la création des segments de code React :
Page |
Délai de chargement moyen (en ms) |
Délai de chargement minimal (en ms) |
Délai de chargement maximal (en ms) |
Table des matières |
3948 |
1165 |
6996 |
Page d'aperçu 1 |
1521 |
246 |
5595 |
Page d'aperçu 2 |
37895 |
20713 |
55070 |
Page détaillée 1 |
13201 |
158 |
31618 |
Page détaillée 2 |
1573 |
45 |
6731 |
Total |
11628 |
45 |
55070 |
Après la création des segments de code React :
Page |
Délai de chargement moyen (en ms) |
Délai de chargement minimal (en ms) |
Délai de chargement maximal (en ms) |
Table des matières |
3840 |
1160 |
6281 |
Page d'aperçu 1 |
810 |
116 |
5457 |
Page d'aperçu 2 |
35250 |
20642 |
53209 |
Page détaillée 1 |
12944 |
110 |
30390 |
Page détaillée 2 |
1052 |
40 |
5252 |
Total |
9454 (-19%) |
40 (-12%) |
53209 (-3%) |
On voit clairement qu'avec ce changement, nous avons réduit de 19 % en moyenne le temps de chargement des pages. Notez cependant que pour ce site web en particulier, nous n'avons créé des segments de code que pour les morceaux de code les plus courants. Il est possible de faire encore mieux.
Alors, qu'attendez-vous ? N'hésitez pas à essayer et constatez par vous-même que votre site web peut gagner en performance !