Il est si facile d’apprendre et d’utiliser Vue.js que n’importe qui peut créer une application simple avec ce framework. Même les novices, avec l’aide de la documentation de Vue, peuvent effectuer le travail. Cependant, quand la complexité entre en jeu, les choses deviennent un peu plus sérieuses. La vérité est que plusieurs composants profondément imbriqués avec un état partagé peuvent rapidement transformer votre application en un gâchis impossible à maintenir.
Le problème principal dans une application complexe est : comment gérer l’état entre les composants sans écrire de code spaghetti ou produire des effets secondaires. Dans ce tutoriel, vous apprendrez comment résoudre ce problème en utilisant Vuex : une bibliothèque de gestion d’état pour créer des applications Vue.js complexes.
Qu’est-ce que Vuex ?
Vuex est une bibliothèque de gestion d’état spécialement conçue pour la construction d’applications Vue.js complexes à grande échelle. Il utilise un magasin global et centralisé pour tous les composants d’une application, tout en tirant parti de son système de réactivité pour des mises à jour instantanées.
Le magasin Vuex est conçu de telle manière qu’il n’est pas possible de changer son état à partir de n’importe quel composant. Cela garantit que l’état ne peut être transformé d’une manière prévisible. Ainsi, votre magasin devient une source de vérité unique : chaque élément de données n’est stocké qu’une seule fois et une lecture unique est faite pour empêcher les composants de l’application de corrompre l’état auquel accèdent les autres composants.
Pourquoi avez-vous besoin de Vuex ?
Vous vous demander peut être : Pourquoi ai-je besoin de Vuex en premier lieu ? Ne pourrai-je pas tout simplement mettre l’état partagé dans un fichier JavaScript normal et l’importer dans mon application Vue.js ?
Bien sûr que vous pouvez, mais par rapport à un objet global, le magasin Vuex a des avantages et des privilèges significatifs :
- Le magasin Vuex est réactif. Une fois que les composants en récupèrent un état, ils vont mettre à jour de manière réactive leurs vues chaque fois que l’état change.
- Les composants ne peuvent pas changer directement l’état du magasin. La seule façon de changer l’état du magasin est d’effectuer explicitement des transformations. Cela garantit que chaque changement d’état laisse un enregistrement traçable, ce qui rend l’application plus facile à déboguer et à tester.
- Vous pouvez facilement déboguer votre application grâce à l’intégration de Vuex avec l’extension DevTools de Vue.
- Le magasin Vuex vous permet de voir comment tout est connecté et affecté dans votre application.
- Il est plus facile de maintenir et de synchroniser l’état entre plusieurs composants, même si la hiérarchie des composants change.
- Vuex permet une communication directe entre les composants.
- Si un composant est détruit, l’état dans le magasin Vuex restera intact.
Premiers pas avec Vuex
Avant de commencer, je veux clarifier plusieurs choses.
Tout d’abord, pour suivre ce tutoriel, vous devez avoir une bonne compréhension de Vue.js et de son système de composants, ou au moins une expérience minimale avec le framework.
De plus, le but de ce tutoriel n’est pas de vous montrer comment construire une application complexe ; le but est de focaliser votre attention sur les concepts de Vuex et sur la façon de les utiliser pour construire des applications complexes. Pour cette raison, je vais utiliser des exemples faciles et très simples, sans aucun code redondant. Une fois que vous aurez compris les concepts de Vuex, vous pourrez les appliquer à n’importe quel niveau de complexité.
Enfin, je vais utiliser la syntaxe ES2015. Maintenant, commençons !
Configuration d’un projet Vuex
La première étape pour commencer avec Vuex est d’avoir Vue.js et Vuex installés sur votre machine. Il y a plusieurs façons de le faire, mais nous allons utiliser le plus simple. Créez simplement un fichier HTML et ajoutez les liens CDN nécessaires :
J’ai utilisé quelque CSS pour rendre les composants plus beaux, mais vous n’avez pas à vous soucier de ce code CSS. Cela ne fait que vous aidez à avoir un aperçu de ce qui se passe. Copiez et collez simplement ce qui suit dans la balise <head> :
Maintenant, créons quelques composants pour s’exercer. Dans la balise <script>, juste au-dessus de la balise de fermeture </ body>, placez le code Vue suivant :
Ici, nous avons un exemple de Vue, un composant mère et deux composants dérivés. Chaque composant a un en-tête « Score: » où nous afficherons l’état de l’application.
La dernière chose que vous devez faire est d’entourer <div> avec id = »app » juste après l’ouverture <body>, puis placez le composant principal à l’intérieur :
Le travail de préparation est maintenant terminé et nous sommes prêts à passer à autre chose.
Exploration de Vuex
Gestion d’état
Dans la réalité, nous traitons la complexité en utilisant des stratégies pour organiser et structurer le contenu que nous voulons utiliser. Nous regroupons des choses connexes dans différentes sections, catégories, etc. C’est comme une bibliothèque, dans laquelle les livres sont catégorisés et placés dans différentes sections afin que nous puissions facilement trouver ce que nous recherchons. Vuex organise les données et la logique de l’application liées à l’état dans quatre groupes ou quatre catégories : état, getters, mutations et actions.
L’état et les mutations sont à la base de tout magasin Vuex :
- state est un objet qui contient l’état des données de l’application.
- mutations est également un objet contenant des méthodes qui affectent l’état.
Les getters et les actions sont comme des projections logiques d’état et de mutations :
- Les getters contiennent des méthodes utilisées pour extraire l’accès à l’état et effectuer des travaux de prétraitement, si nécessaire (calcul de données, filtrage, etc.).
- Les actions sont des méthodes utilisées pour déclencher des mutations et exécuter du code asynchrone.
Explorons le diagramme suivant pour rendre les choses un peu plus claires :
Sur le côté gauche, nous avons un exemple de magasin Vuex que nous allons créer plus tard dans ce tutoriel. Sur le côté droit, nous avons un diagramme de flux de travail Vuex qui montre comment les différents éléments de Vuex fonctionnent ensemble et communiquent entre eux.
Pour changer d’état, un composant particulier de Vue doit effectuer des mutations (exemple this.$ store.commit (‘increment’, 3)), puis, ces mutations changent l’état (le score devient 3). Après cela, les getters sont automatiquement mis à jour grâce au système réactif de Vue, et ils rendent les mises à jour dans la vue du composant (avec this.$ store.getters.score).
Les mutations ne peuvent pas exécuter de code asynchrone, car cela rendrait impossible l’enregistrement et le suivi des modifications dans les outils de débogage tels que Vue DevTools. Pour utiliser la logique asynchrone, vous devez le mettre en actions. Dans ce cas, un composant va d’abord distribuer des actions (par exemple, this.$ store.dispatch (‘incrementScore’, 3000)) où le code asynchrone est exécuté, puis ces actions vont effectuer des mutations, ce qui fera muter l’état.
Création de la base du magasin Vuex
Maintenant que nous avons exploré le fonctionnement de Vuex, créons la base de notre magasin Vuex. Placez le code suivant au-dessus de l’enregistrement du composant ChildB :
Pour fournir un accès global au magasin Vuex à partir de chaque composant, nous devons ajouter la propriété store dans l’instance de Vue :
Maintenant, nous pouvons accéder au magasin de chaque composant avec la variable this.$store.
Jusqu’à présent, si vous ouvrez le projet avec CodePen dans le navigateur, vous devriez voir le résultat suivant.
Propriétés de l’état
L’objet de l’état contient toutes les données partagées dans votre application. Bien sûr, chaque composant peut si nécessaire avoir son propre état privé.
Imaginez que vous voulez construire une application de jeu, et vous avez besoin d’une variable pour stocker le score du jeu. Donc, vous le mettez dans l’objet d’état :
Maintenant, vous pouvez accéder directement au score de l’état. Revenons aux composants et réutilisons les données du magasin. Afin de pouvoir réutiliser les données réactives de l’état du magasin, vous devez utiliser les propriétés calculées. Créons donc une propriété calculée score () dans le composant principal :
Dans le modèle du composant principal, mettez l’expression {{score}}:
Et maintenant, faites de même pour les deux composants dérivés.
Vuex est si intelligent qu’il fera tout le travail pour mettre à jour de manière réactive la propriété score chaque fois que l’état change. Essayez de changer la valeur du score et voyez comment le résultat est mis à jour dans les trois composants.
Création des Getters
Il est, bien sûr, bon que vous puissiez réutiliser le mot clé this.$store.state à l’intérieur des composants, comme vous l’avez vu ci-dessus. Mais imaginez les scénarios suivants :
- Dans une application à grande échelle, où plusieurs composants accèdent à l’état du magasin en utilisant this.$Store.state.score, vous décidez de changer le nom du score. Cela signifie que vous devez changer le nom de la variable dans chaque composant qui l’utilise !
- Vous voulez utiliser une valeur calculée de l’état. Par exemple, disons que vous voulez donner aux joueurs un bonus de 10 points lorsque le score atteint 100 points. Ainsi, lorsque le score atteint 100 points, un bonus de 10 points est ajouté. Cela signifie que chaque composant doit contenir une fonction qui réutilise le score et l’incrémente de 10. Vous aurez un code répété dans chaque composant, ce qui n’est pas bon du tout !
Heureusement, Vuex offre une solution de travail pour gérer de telles situations. Imaginez le getter centralisé qui accède à l’état du magasin et fournit une fonction getter à chacun des éléments de l’état. Si nécessaire, ce getter peut appliquer un calcul à l’élément de l’état. Et si vous avez besoin de changer les noms de certaines propriétés de l’état, vous ne pouvez les changez que dans un seul endroit, dans ce getter.
Créons un getter score () :
Un getter reçoit state comme premier argument, puis l’utilise pour accéder aux propriétés de l’état.
Remarque : Les getters reçoivent également des getters comme deuxième argument. Vous pouvez l’utiliser pour accéder aux autres getters dans le magasin.
Dans tous les composants, modifiez la propriété calculée score () pour utiliser le getter score () au lieu du score de l’état directement.
Maintenant, si vous décidez de changer le score en résultat, vous devez le mettre à jour en un seul endroit: dans le getter score ().
Création des mutations
Les mutations sont la seule façon permise de changer l’état. Déclencher des changements signifie simplement effectuer des mutations dans les méthodes de composants.
Une mutation est à peu près une fonction de gestionnaire d’événements définie par son nom. Les fonctions du gestionnaire de mutation reçoivent un état en tant que premier argument. Vous pouvez également passer un deuxième argument supplémentaire, appelé charge utile de la mutation.
Créons une mutation increment () :
Les mutations ne peuvent pas être appelées directement ! Pour effectuer une mutation, vous devez appeler la méthode commit () avec le nom de la mutation correspondante et d’éventuels paramètres supplémentaires. Il peut s’agir d’un seul, comme l’étape dans notre cas, ou il peut y en avoir plusieurs enveloppés dans un objet.
Utilisons la mutation increment () dans les deux composants dérivés en créant une méthode nommée changeScore () :
Nous effectuons une mutation au lieu de changer this.$Store.state.score directement, parce que nous voulons suivre explicitement le changement apporté par la mutation. De cette façon, nous rendons notre logique applicative plus transparente, traçable et facile à raisonner. En outre, il permet de mettre en œuvre des outils, tels que Vue DevTools ou Vuetron, qui peuvent enregistrer toutes les mutations, prendre des instantanés d’état et effectuer un débogage temporel.
Maintenant, essayons d’appliquer la méthode changeScore (). Dans chaque modèle des deux composants dérivés, créez un bouton et ajoutez-y un écouteur d’événement click :
Lorsque vous cliquez sur le bouton, l’état sera incrémenté de 3 et cette modification sera répercutée dans tous les composants. Nous avons maintenant réussi à obtenir une communication directe entre les composants, ce qui n’est pas possible avec le mécanisme intégré «props down, events up» de Vue.js.
Création des actions
Une action est juste une fonction qui commet une mutation. Il change indirectement l’état, ce qui permet l’exécution d’opérations asynchrones.
Créons une action incrementScore () :
Les actions obtiennent le contexte en tant que premier paramètre, qui contient toutes les méthodes et propriétés du magasin. Habituellement, nous extrayons simplement les parties dont nous avons besoin en utilisant la déstructuration d’argument ES2015. La méthode commit est celle dont nous avons très souvent besoin. Les actions obtiennent également un second argument de charge utile, tout comme les mutations.
Dans le composant ChildB, modifiez la méthode changeScore () :
Pour faire appel à une action, nous utilisons la méthode dispatch () avec le nom de l’action correspondante et des paramètres supplémentaires, tout comme avec les mutations.
Maintenant, le bouton Change Score du composant ChildA incrémente le score de 3. Le bouton identique du composant ChildB fera de même, mais après un délai de 3 secondes. Dans le premier cas, nous exécutons du code synchrone et nous utilisons une mutation, mais dans le second cas, nous exécutons du code asynchrone, et nous devons utiliser une action à la place.
Assistants cartographique de Vuex
Vuex offre des aides utiles qui peuvent rationaliser le processus de création d’état, de getters, de mutations et d’actions. Au lieu d’écrire ces fonctions manuellement, nous pouvons dire à Vuex de les créer pour nous. Voyons voir comment ça fonctionne.
Au lieu d’écrire la propriété calculée score () comme ceci :
Nous utilisons simplement l’assistant mapState () comme ceci :
Et la propriété score () est créée automatiquement pour nous.
La même chose est vraie pour les getters, les mutations et les actions.
Pour créer le getter score (), nous utilisons l’assistant mapGetters () :
Pour créer la méthode changeScore (), nous utilisons l’assistant mapMutations () comme ceci :
Lorsqu’il est utilisé pour des mutations et des actions avec l’argument de charge utile, nous devons transmettre cet argument dans le modèle où nous définissons le gestionnaire d’événements :
Si nous voulons changeScore () pour utiliser une action au lieu d’une mutation, nous utilisons mapActions () comme ceci :
Encore une fois, nous devons définir le délai dans le gestionnaire d’événements :
Remarque : Tous les assistants cartographiques renvoient un objet. Donc, si nous voulons les utiliser en combinaison avec d’autres propriétés ou méthodes calculées, nous devons les fusionner en un seul objet. Heureusement, avec l’opérateur de propagation d’objet (…), nous pouvons le faire sans utiliser l’utilitaire.
Rendre le magasin plus modulaire
Il semble que le problème de la complexité entrave constamment notre chemin. Nous l’avons résolu auparavant en créant le magasin Vuex, où nous avons facilité la gestion des états et la communication des composants. Dans ce magasin, nous avons tout dans un même endroit, facile à manipuler et facile à raisonner.
Cependant, à mesure que notre application se développe, ce fichier de stockage facile à gérer devient de plus en plus volumineux et, par conséquent, plus difficile à gérer. Encore une fois, nous avons besoin de stratégies et de techniques pour améliorer la structure de l’application en la rendant facile à maintenir. Dans cette section, nous explorerons plusieurs techniques qui peuvent nous aider dans cette démarche.
Utilisation de modules Vuex
Vuex nous permet de diviser l’objet magasin en modules distincts. Chaque module peut contenir son propre état, ces mutations, ces actions, ces getters et d’autres modules imbriqués. Après avoir créé les modules nécessaires, nous les enregistrons dans le magasin.
Essayons de l’appliquer :
Dans l’exemple ci-dessus, nous avons créé deux modules, un pour chaque composant dérivé. Les modules sont simplement des objets simples, que nous enregistrons comme scoreBoard et resultBoard dans l’objet modules à l’intérieur du magasin. Le code de childA est le même que celui du magasin des exemples précédents. Dans le code pour childB, nous ajoutons quelques changements dans les valeurs et les noms.
Modifions maintenant le composant ChildB pour refléter les changements dans le module resultBoard.
Dans le composant ChildA, la seule chose que nous devons modifier est la méthode changeScore () :
Comme vous pouvez le voir, la division du magasin en modules le rend beaucoup plus léger et plus gérable, tout en gardant ses grandes fonctionnalités.
Module des espaces nommées
Si vous souhaitez ou vous devez utiliser un seul et même nom pour une propriété ou une méthode particulière dans vos modules, vous devez envisager de les insérer dans un espace de noms. Sinon, vous pourrez observer des effets secondaires étranges, tels que l’exécution de toutes les actions portant les mêmes noms ou l’obtention de valeurs d’état erronées.
Pour nommer un module Vuex, vous devez simplement définir la propriété namespaced sur true.
Dans l’exemple ci-dessus, nous avons donné des noms de propriétés et de méthodes identiques pour les deux modules. Et maintenant nous pouvons utiliser une propriété ou une méthode préfixée par le nom du module. Par exemple, si nous voulons utiliser le getter score () du module resultBoard, nous l’écrivons comme suit : resultBoard/score. Si nous voulons le getter score () du module scoreBoard, nous le tapons comme suit : scoreBoard / score.
Modifions maintenant nos composants pour refléter les changements que nous avons faits.
Comme vous pouvez le voir dans notre CodePen example, nous pouvons maintenant utiliser la méthode ou la propriété que nous voulons pour obtenir le résultat que nous attendons.
Fraction du magasin Vuex en fichiers séparés
Dans la section précédente, nous avons amélioré la structure de l’application dans une certaine mesure en séparant le magasin en modules. Nous avons rendu le magasin plus propre et plus organisé, mais tout le code du magasin et ses modules se trouvent dans le même gros fichier.
Donc la prochaine étape logique est de diviser le magasin Vuex en fichiers séparés. L’idée est d’avoir un fichier individuel pour le magasin lui-même et un pour chacun de ses objets, y compris les modules. Cela signifie avoir des fichiers séparés pour l’état, les getters, les mutations, les actions, et pour chaque module individuel (store.js, state.js, getters.js, etc.). Vous pouvez voir un exemple de cette structure à la fin de la prochaine section.
Utilisation de composants en vue unique
Nous avons rendu le magasin Vuex aussi modulaire que possible. La prochaine chose que nous pouvons faire c’est d’appliquer aussi la même stratégie aux composants Vue.js. Nous pouvons placer chaque composant dans un seul fichier autonome avec une extension .vue.
Donc, dans notre cas, nous aurons trois fichiers : Parent.vue, ChildA.vue, et ChildB.vue.
Enfin, si nous combinons les trois techniques, nous obtiendrons la structure suivante :
Résumé
Récapitulons quelques points principaux dont vous devez vous souvenir à propos de Vuex :
Vuex est une bibliothèque de gestion d’état qui nous aide à construire des applications complexes à grande échelle. Il utilise un magasin global et centralisé pour tous les composants d’une application. Mise à part l’état, nous utilisons des getters. Les getters ressemblent à peu près aux propriétés calculées et constituent une solution idéale lorsque nous devons filtrer ou calculer quelque chose à l’exécution.
Le magasin Vuex est réactif et les composants ne peuvent pas modifier directement l’état du magasin. La seule façon de modifier l’état c’est d’effectuer des mutations, qui sont des transactions synchrones. Chaque mutation ne doit effectuer qu’une seule action, doit être aussi simple que possible et n’est responsable que de la mise à jour d’une partie de l’état.
La logique asynchrone devrait être enveloppée dans des actions. Chaque action peut effectuer une ou plusieurs mutations, et une mutation peut être commise par plus d’une action. Les actions peuvent être complexes, mais elles ne changent jamais directement l’état.
Enfin, la modularité est la clé de la durabilité. Pour faire face à la complexité et rendre notre code modulaire, nous utilisons le principe « diviser pour régner » et la technique de partage de code.
Conclusion
C’est tout! Vous connaissez déjà les principaux concepts de Vuex et vous êtes prêt à les appliquer dans la pratique.