Vue.js 2.0 : petit tutoriel (volume 5)

Dans la suite de ce tutoriel nous abordons le routage dans Vue.js avec le module vue-router.

Si vous avez manqué le début :

  • Volume 1 : pour créer un projet, créer un composant et transmettre des données d’un composant parent à un composant enfant ;
  • Volume 2 : pour apprendre à modifier les données d’un composant et de quelle façon invoquer un service REST ;
  • Volume 3 : pour transmettre des données d’un composant enfant à un composant parent et découvrir la communication par événements ;
  • Volume 4 : pour découvrir le pattern gestionnaire d’état et une implémentation Vue.js officielle avec Vuex.

Le code source est accessible ici. Tout ce qui suit fait référence au contenu des répertoires vuejs-tuto/src/pages/chap9/ et vuejs-tuto/src/components/chap9/

Routage et SPA

Vue.js peut être utilisé pour créer une “classique” Multi Page Application (MPA) comme une Single Page Application (SPA) voire une “application hybride” en n’utilisant Vue.js que pour quelques fonctionnalités.

L’utilisation d’une SPA signifie que l’application n’est constituée que d’une seule page, une seule ressource identifiée par une unique URL : même si visuellement on peut avoir le sentiment de changer de page (en sélectionnant un menu, un onglet ou autre) , il n’y a pas en fait de véritablement navigation dans l’application au sens où on l’entend habituellement, avec un changement d’URL et un appel à chaque clic pour récupérer une nouvelle page construite côté serveur.

Les SPAs permettent en particulier de proposer aux utilisateurs d’une application web une expérience proche de celle d’une application mobile, tout en facilitant par exemple la diffusion de l’application puisqu’on est alors libéré des contraintes des stores et de leur processus de livraison ou encore de l’obligation de mettre à jour l’application. Cette “meilleure” expérience se traduit notamment par le fait qu’il n’y a plus besoin de faire de nombreux appels serveurs pour mettre à jour la page dès qu’une action est effectuée dans l’application.

Pour éviter de recharger la SPA inutilement à chaque clic ou encore permettre qu’un favori enregistré dans le navigateur par l’utilisateur l’amène directement à la “page” correspondante dans la SPA, on peut “simuler” plusieurs ressources et leur url au travers d’un fragment identifier (le caractère #) ajouté dans l’url : c’est le “hashmode”.

Pour permettre l’indexation par les moteurs de recherche, faciliter les redirections, etc. on préférera l’utilisation d’URL classiques à travers l’API History.js d’HTML5.

Les frameworks JavaScript permettant de créer des SPA proposent un module additionnel pour mettre en place cette navigation. C’est justement le cas de Vue.js avec son module vue-router qui découpe une SPA en différentes routes selon le mode “hashmode” ou le mode “history”.

vue-router

Pour installer ce module, cela se résume à la commande npm :

npm i –save vue-router

Pour définir les différentes routes de l’application, il suffit de créer une instance de VueRouter en spécifiant le mode à utiliser (celui par défaut, basé sur hashmode, ou “history” si on souhaite manipuler de véritables URLs) ainsi que la liste des chemins d’accès et des composants à afficher lorsque ce chemin est accédé, le tout associé au nom de la route définie :

src/pages/chap9/AppRoutes.js
src/pages/chap9/AppRoutes.js

Pour utiliser ces routes dans l’application, il n’y a qu’à l’indiquer à l’instance principale de Vue :

src/main.js
src/main.js

Pour permettre d’afficher le composant associé à un chemin donné, on passe par le composant router-view :

src/App.vue
src/App.vue

Résultat si on accès à l’url http://localhost:8083/vuejs-tuto/chap1 :

Si on tape http://localhost:8083/vuejs-tuto/chap2 :

Accéder à l’URL courante

L’utilisation de vue-router met à disposition l’objet $route, c’est-à-dire l’URL de la ressource accédée. Comme pour toute URL, il est possible de transmettre des paramètres qu’on peut récupérer dans le composant affiché via this.$route.query.[NOM_DU_PARAMETRE] comme dans l’exemple suivant :

src/components/chap9/Hello.vue
src/components/chap9/Hello.vue

Après avoir ajouté la route qui va bien,

src/pages/chap9/AppRoutes.js
src/pages/chap9/AppRoutes.js

en allant sur http://localhost:8083/vuejs-tuto/chap9?lastname=Simpson&firstname=Maggie on obtient alors :

Si on souhaite manipuler des URL dynamiques, on doit commencer par identifier dans la définition de la route quelles portions de l’URL sont variables. Par exemple dans le chemin /vuejs-tuto/chap9/:id, :id pourra être remplacé par n’importe quelle valeur et quelle que soit cette valeur c’est la même instance de composant qui sera utilisée :

src/pages/chap9/AppRoutes.js
src/pages/chap9/AppRoutes.js

Dans le composant, on accède à la valeur dynamique de l’url via this.$route.params.[NOMELEMENTDYNAMIQUE]

src/components/chap9/Hello.vue
src/components/chap9/Hello.vue

En allant sur http://localhost:8083/vuejs-tuto/chap9/123456789?lastname=Simpson&firstname=Maggie on obtient alors :

Si on récupère la valeur dynamique en accédant explicitement à l’URL dans le composant, on limite son utilisation puisqu’on ne pourra pas l’inclure simplement dans une page en transmettant la valeur dynamique à travers un attribut de l’élément HTML comme vu dans le premier volet de ce tutoriel : pour pallier cette limitation il suffit d’ajouter dans la définition de la route props: true afin que la portion dynamique de l’URL soit accédée via les propriétés du composant.

src/pages/chap9/AppRoutes.js
src/pages/chap9/AppRoutes.js

 

src/components/chap9/Hello.vue
src/components/chap9/Hello.vue

La navigation entre les pages passe par l’utilisation du composant router-link. Si on ajoute un composant Menu.vue affichant une liste de liens pour atteindre les différentes pages de l’application :

src/components/chap9/Menu.vue
src/components/chap9/Menu.vue

On obtient :

Notez qu’on peut spécifier l’URL cible soit via le chemin soit via le nom de la route définie.

Lazy loading

Plutôt que de charger l’ensemble des composants de l’application en une fois au premier accès, il est préférable en termes de performance de ne charger que les composants effectivement utilisés dans la page actuellement consultée, en récupérant leur définition de façon asynchrone, uniquement lorsque c’est nécessaire.

Vue.js permet de charger des composants à la volée à travers une fonctionnalité de composant asynchrone (dans laquelle on expose une fonction retournant une promesse important le composant), fonctionnalité que l’on peut associer à celle de code splitting de webpack (qui permet de découper le code d’une application en plusieurs modules JavaScript) : au final, lorsque l’unique fichier constituant la SPA est récupéré au premier accès à l’application, ce fichier ne contient pas le code javascript permettant de construire le(s) composant(s) devant être chargé(s) de façon différée. Lorsqu’on ouvre une page devant afficher un composant dont la définition ne fait pas partie du fichier principal de l’application, une requête supplémentaire est lancée auprès du serveur pour récupérer le module JavaScript nécessaire à la construction du composant manquant.

Dans la définition des routes, il suffit de spécifier le composant à utiliser de la façon suivante :

src/pages/chap9/AppRoutes.js
src/pages/chap9/AppRoutes.js

Avec cette configuration, si on observe les différentes requêtes qui sont déclenchées à partir du navigateur, on peut voir qu’une requête supplémentaire est lancée lorsque le lazy loading est activé :

Requêtes effectuées SANS lazy loading
Requêtes effectuées SANS lazy loading

 

Requêtes effectuées AVEC lazy loading
Requêtes effectuées AVEC lazy loading

Hooks

Vue-router offre la possibilité de facilement intervenir à toutes les étapes constituant la navigation d’une page à une autre, à travers les hooks :

  • beforeEach et afterEach qui s’appliquent à une instance de vue-router et qui s’exécutent à tous les changements d‘URL, juste avant le début et/ou juste après la fin de la navigation ; on les déclarera au même endroit que la définition des routes ; c’est par exemple à ce niveau qu’on peut tester si un utilisateur a le droit d’accéder ou non à la page ciblée ;
  • beforeEnter, qui s’applique directement sur une route pour exécuter une portion de code spécifique à la route en question ;
  • beforeRouteEnter, beforeRouteUpdate et beforeRouteLeave qui sont disponibles au niveau de chaque composant ; on passera par exemple par beforeRouteEnter lorsqu’on souhaite bloquer la navigation tant qu’on n’a pas reçu de la part du serveur toutes les données nécessaires à l’affichage des composants de la page cible,  beforeRouteUpdate pour effectuer certaines opérations sur une instance de composant ré-utilisée lorsqu’une URL dynamique est ouverte, beforeRouteLeave pour par exemple afficher un message prévenant l’utilisateur qu’il perdra les informations saisies s’il ne les sauvegarde pas avant de quitter la page courante.

Tous ces hooks prennent en argument la route de destination (to), la route de provenance (from), et une fonction à appeler explicitement pour sortir du hook un fois celui-ci résolu (next, à l’exception de afterEach qui ne dispose pas de cette fonction puisqu’il est le dernier hook de la chaîne à s’exécuter). Par défaut, cette fonction permet simplement d’exécuter le hook suivant de la chaîne mais selon le paramètre transmis elle permet aussi d’effectuer une redirection vers une autre page ou tout simplement d’annuler la navigation.

L’exécution des hooks s’enchaîne selon la chronologie suivante :

Intégrons les hooks à notre application :

Définition des hooks globaux et spécifiques aux routes dans src/pages/chap9/AppRoutes.js
Définition des hooks globaux et spécifiques aux routes dans src/pages/chap9/AppRoutes.js

 

Définition des hooks spécifiques à un composant dans src/components/chap9/Hello.vue
Définition des hooks spécifiques à un composant dans src/components/chap9/Hello.vue

Ajoutons à présent dans notre composant Menu.vue un second lien vers la page pagechap9 mais en lui transmettant des paramètres différents :

src/components/chap9/Menu.vue
src/components/chap9/Menu.vue

Observons ce qui se passe :
  1. Accès à http://localhost:8083/vuejs-tuto/chap1 : seuls les hooks globaux sont invoqués, puisqu’aucun hook spécifique à la route accédée ou spécifique au composant affiché n’a été paramétré.
  2. Accès à http://localhost:8083/vuejs-tuto/chap2 à partir de http://localhost:8083/vuejs-tuto/chap1 : même comportement que précédemment, pour les mêmes raisons.
  3. Accès à http://localhost:8083/vuejs-tuto/chap9/123123123?firstname=Maggie&lastname=Simpson à partir de http://localhost:8083/vuejs-tuto/chap2 : les hooks globaux sont exécutés ainsi que ceux spécifiques à la route pagechap9 ; dans les hooks spécifiques au composant affiché seul beforeRouteEnter est appelé puisque le composant est nouvellement activé.
  4. Accès à http://localhost:8083/vuejs-tuto/chap9/555555555?firstname=Montgomery&lastname=Burns à partir de http://localhost:8083/vuejs-tuto/chap9/123123123?firstname=Maggie&lastname=Simpson : les hooks globaux sont une fois de plus exécutés mais comme on reste sur la même page ceux spécifiques à la route ne le sont pas ; les paramètres de l’URL changent et comme on affiche la même instance de composant que celle déjà utilisée dans la page précédente seul le hook beforeRouteUpdate du composant est invoqué.
  5. Accès à http://localhost:8083/vuejs-tuto/chap2 à partir de http://localhost:8083/vuejs-tuto/chap9/555555555?firstname=Montgomery&lastname=Burns : les hooks globaux sont invoqués mais juste après le beforeRouteLeave du composant de la page d’origine, exécuté puisqu’on change de route.

To be continued…

Dans ce cinquième volet vous avez pu constater (enfin, je l’espère 😉 ) qu’il est très simple de gérer la navigation applicative avec vue-router : la fonctionnalité de lazy loading apporte un vrai bénéfice tout en étant particulièrement simple à mettre en place, les hooks offrent un accès aisé à toutes les étapes de résolution de la navigation. Vous avez à présent l’essentiel des connaissances nécessaires pour mettre en oeuvre une application d’entreprise avec Vue.js. À la prochaine ! 😉