Routeur Angular : Chargement à la demande déclaratif

Philippe Martin
Angular
Published in
5 min readOct 8, 2016

--

Traduction de l’article “Angular Router: Declarative Lazy Loadingde Victor Savkin.

Angular 2 a été construit avec l’accent sur le mobile. Voilà pourquoi nous faisons beaucoup d’efforts pour réduire la taille des applications Angular 2 compilées et empaquetées. Une des techniques que nous utilisons énormément est l’élimination de code mort, qui a permis de réduire la taille d’une application “Hello world” à seulement 20K. C’est la moitié de la taille d’une application Angular 1 similaire — un résultat impressionnant !

À un certain point, cependant, notre application sera suffisamment grande et, même avec cette technique, le fichier de l’application sera trop gros pour être chargé en une seule fois. C’est ici que le chargement à la demande entre en jeu.

Cet article est basé sur le livre « Angular 2 Router » que vous pouvez trouver icihttps://leanpub.com/router. Le livre va au-delà d’un simple guide de démarrage et décrit le routeur en profondeur. Le modèle mental, les contraintes de design et les subtilités de l’API — tout est couvert. Si vous aimez l’article, consultez le livre !

Le chargement à la demande accélère le temps de chargement de notre application en découpant son fichier en plusieurs paquets, et en les chargeant à la demande. Nous avons conçu le routeur pour rendre le chargement à la demande simple et facile.

Dans cet article nous allons utiliser une application de mail comme exemple. Cette application a deux parties : messages et contacts. Au démarrage, notre application affiche des messages. En cliquant sur le bouton contacts elle affiche les contacts.

Commençons par esquisser notre application.

Le bouton permettant d’afficher l’UI des contacts peut ressembler à ceci :

En outre, nous pouvons aussi prendre en compte les liens vers des contacts individuels, de cette manière :

Dans le code d’exemple ci-dessus toutes les routes et composants sont définis ensemble, dans le même fichier. Ceci par souci de simplicité. La façon dont les composants sont rangés n’est pas vraiment important, étant donné qu’après la compilation nous aurons un seul fichier empaqueté ‘main.bundle.js’, qui contiendra l’application complète.

Juste un problème

Il y a un problème avec cette configuration : même si ContactsCmp et ContactCmp ne sont pas affichés au chargement, ils sont tout de même empaquetés avec la partie principale de l’application. Résultat, le paquet initial est plus grand que nécessaire.

Deux composants supplémentaires peuvent sembler ne pas être un gros problème, mais dans une application réelle le module contacts peut inclure des douzaines voire des centaines de composants, avec tous les services et fonctions utilitaires dont ils ont besoin.

Une meilleure configuration serait d’extraire le code relatif aux contacts dans un module séparé et de le charger à la demande. Voyons comment procéder.

Chargement à la demande

Nous commençons par extraire tous les composants relatifs aux contacts ainsi que les routes dans un fichier séparé.

Dans Angular 2, un ngModule est une partie d’application qui peut être empaquetée et chargée indépendamment. Nous en avons donc défini un dans le code ci-dessus.

Référencer un module chargé à la demande

Maintenant, après avoir extrait le module contacts, nous devons mettre à jour le module principal pour qu’il référence celui nouvellement extrait.

La propriété loadChildren indique au routeur de récupérer ‘contacts.bundle.js’ lorsque et seulement lorsque l’utilisateur navigue vers ‘contacts’, puis fusionne les deux configurations de routeur et, enfin, active les composants nécessaires.

En faisant cela, nous découpons l’unique paquet en deux.

Le bootstrap charge uniquement le paquet principal. Le routeur chargera le paquet contacts lorsqu’il sera nécessaire.

Notez qu’à part la configuration du routeur, nous n’avons rien à modifier dans l’application après l’avoir découpée en plusieurs paquets : les liens existants restent inchangés.

Liens profonds

Mais c’est mieux que ça ! Le routeur supporte aussi les liens profonds dans les modules chargés à la demande.

Pour voir ce que je veux dire imaginez que le module contacts charge à la demande un autre module.

Imaginez que nous ayons le lien suivant dans la partie principale de notre application.

En cliquant sur le lien, le routeur va d’abord récupérer le module contacts, puis le module détails. Après cela il va fusionner toutes les configurations et instancier les composants nécessaires. Une fois de plus, du point de vue du lien la façon dont nous empaquetons notre application ne fait aucune différence. Ça marche, c’est tout.

Génération synchrone des liens

La directive RouterLink fait plus que prendre en charge les clics. Il définit aussi les attributs href des balises <a>, pour que l’utilisateur puisse cliquer-droit et “Ouvrir un lien dans un nouvel onglet”.

Par exemple, la directive ci-dessus va définir l’attribut href de l’ancre à ‘/contacts/13/detail;full=true’. Et il va faire cela de manière synchrone, sans charger les configurations des paquets contacts ou détails. Lorsque l’utilisateur cliquera effectivement sur le lien, le routeur chargera toutes les configurations nécessaires pour exécuter la navigation.

La navigation est basée sur l’URL

Les liens profonds dans des modules chargés à la demande et la génération synchrone de liens sont possibles uniquement parce que la navigation du routeur est basée sur l’URL. Puisque le routeur n’a pas la notion de noms de routes, il n’a à utiliser aucune configuration pour générer les liens. Ce que nous passons à routerLink (par exemple [‘/contacts’, id, ‘detail’, {full: true}]) est simplement un tableau de segments d’URL. En d’autres termes, la génération de liens est purement mécanique et indépendante de l’application.

Ceci est une décision de conception importante que nous avons prise tôt car nous savions que le chargement à la demande serait un cas d’utilisation essentiel pour utiliser le routeur.

Personnaliser le chargeur de module

Le chargeur de module d’application natif utilise SystemJS. Mais nous pouvons fournir notre propre implémentation de chargeur de cette manière :

Finalement, vous n’êtes pas obligé d’utiliser le chargeur. À la place, vous pouvez fournir un callback que la route utilisera pour récupérer le module.

Pré-charger des modules

Par défaut, le routeur Angular charge uniquement les modules lorsqu’ils sont nécessaires, mais nous pourrions vouloir pré-charger certains modules pour qu’ils soient prêts à l’utilisation.

Il n’est pas difficile d’écrire un service qui examine la configuration du routeur et télécharge de manière sélective des modules en arrière-plan. Le routeur peut alors les récupérer dans le cache et naviguer instantanément.

Aller plus loin

Si vous désirez en savoir plus sur le routeur Angular, consultez le livre que j’ai écrit sur le sujet : https://leanpub.com/router.

Il va bien au-delà d’un simple guide de démarrage et explore la bibliothèque en profondeur, dont le modèle mental, les contraintes de design, les subtilités de l’API, et plus encore. Il révèle des informations précieuses sur le pourquoi du fonctionnement du routeur.

Suivez @victorsavkin pour en savoir plus sur Angular et TypeScript.

--

--