Comment transmettre des données entre des composants dans Vue.js
Publié: 2022-03-10Le partage de données entre les composants est l'une des fonctionnalités essentielles de VueJS. Il vous permet de concevoir un projet plus modulaire, de contrôler les étendues de données et de créer un flux naturel de données dans votre application.
À moins que vous ne créiez l'intégralité de votre application Vue dans un seul composant (ce qui n'aurait aucun sens), vous rencontrerez des situations dans lesquelles vous devrez partager des données entre les composants.
À la fin de ce didacticiel, vous connaîtrez trois façons d'y parvenir.
- Utiliser des accessoires pour partager des données de parent à enfant,
- Émettre des événements personnalisés pour partager des données de l'enfant au parent,
- Utilisation de Vuex pour créer un état partagé au niveau de l'application.
D'accord - allons-y directement!
Construire une application avec Nuxt
Avec Spotify, vos amis peuvent vérifier sur quoi vous brouillez. Et si le reste de l'Internet pouvait aussi expérimenter votre algorithme ? Apprenez à composer votre propre application pour partager ce que vous écoutez sur Spotify en utilisant Vue.js et Nuxt. Lire un article connexe →
1. Utiliser des accessoires pour partager des données de parent à enfant
Les props VueJS sont le moyen le plus simple de partager des données entre les composants. Les accessoires sont des attributs personnalisés que nous pouvons donner à un composant. Ensuite, dans notre modèle, nous pouvons attribuer des valeurs à ces attributs et - BAM - nous transmettons des données d'un parent à un composant enfant !
Par exemple, supposons que nous travaillions sur une page de profil utilisateur et que nous souhaitions qu'un composant enfant accepte un accessoire de nom d'utilisateur. Nous aurons besoin de deux composants.
- Le composant enfant acceptant la prop, appelons ceci
AccountInfo.vue
. - Le composant parent passant le prop, appelons cela
ProfilePage.vue
.
Dans AccountInfo.vue
, nous pouvons déclarer les props qu'il accepte en utilisant l'option props . Donc, à l'intérieur des options du composant, faisons en sorte qu'il ressemble à ce qui suit.
// AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>
Ensuite, pour transmettre réellement les données du parent ( ProfilePage.vue
), nous les transmettons comme un attribut personnalisé.
// ProfilePage.vue <account-info username='matt' />
Maintenant, si nous chargeons notre page, nous pouvons voir que notre composant AccountInfo
restitue correctement la valeur transmise par son parent.
Comme lorsque nous travaillons avec d'autres directives VueJS, nous pouvons utiliser v-bind pour transmettre dynamiquement des props. Par exemple, disons que nous voulons définir la prop username comme étant égale à une variable. Nous pouvons accomplir cela en utilisant un raccourci pour la directive v-bind (ou simplement :
pour faire court). Le code ressemblerait un peu à ceci :
<template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>
Cela signifie que nous pouvons modifier nos données et que tous les accessoires enfants utilisant cette valeur seront également mis à jour.
Conseil : vérifiez toujours vos accessoires
Si vous cherchez à écrire du code Vue plus clair, une technique importante consiste à vérifier vos accessoires. En bref, cela signifie que vous devez spécifier les exigences de votre accessoire (c'est-à-dire le type, le format, etc.). Si l'une de ces conditions n'est pas remplie (par exemple si le prop reçoit un type incorrect), Vue affichera un avertissement.
Disons que nous voulons que notre accessoire de nom d'utilisateur n'accepte que les chaînes. Nous aurions à modifier notre objet props pour qu'il ressemble à ceci :
export default { props: { username: String } }
La vérification des accessoires est essentielle lorsque vous travaillez dans des applications Vue à grande échelle ou lors de la conception de plugins. Cela permet de s'assurer que tout le monde est sur la même page et d'utiliser les accessoires comme prévu.
Pour une liste complète des vérifications que nous pouvons inclure sur les accessoires, je vous recommande vivement de consulter la documentation officielle pour un examen approfondi.
Conseil : suivez les conventions de dénomination des accessoires
Selon le guide de style VueJS, la meilleure façon de nommer vos accessoires est d'utiliser camelCase
lors de leur déclaration dans votre script et kebab-case lors de leur référencement dans le code du modèle.
Le raisonnement derrière cela est en fait assez simple. En Javascript, camelCase
est la convention de dénomination standard et en HTML, c'est kebab-case.
Ainsi, Vue recommande de s'en tenir aux normes de chaque langue. Heureusement, Vue est capable de convertir automatiquement entre les deux styles, il n'y a donc pas de configuration supplémentaire pour les développeurs.
// GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }
2. Émission d'événements pour partager des données d'un enfant à un parent
Maintenant que nous avons des données transmises dans la hiérarchie, passons-les dans l'autre sens : d'un composant enfant à un parent. Nous ne pouvons pas utiliser d'accessoires, mais nous pouvons utiliser des événements et des écouteurs personnalisés.
Chaque instance de Vue peut appeler une méthode .$emit(eventName)
qui déclenche un événement. Ensuite, nous pouvons écouter cet événement de la même manière que n'importe quel autre, en utilisant la directive v-on.
Créer un événement personnalisé
Construisons sur notre exemple de profil utilisateur en ajoutant un bouton qui change le nom d'utilisateur. Dans notre composant enfant ( AccountInfo.vue
), créons le bouton.
Ensuite, lorsque ce bouton est cliqué, nous émettrons un événement appelé changeUsername
.
<template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>
À l'intérieur du parent, nous gérons cet événement et modifions la variable user.username
. Comme nous en parlions plus tôt, nous pouvons écouter les événements en utilisant la directive v-on ou "@" en abrégé.
<template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>
Essayons. Vous devriez voir que lorsque vous cliquez sur le bouton, le nom d'utilisateur devient "nouveau nom".
Conseil : les événements personnalisés peuvent accepter des arguments
Le cas d'utilisation le plus courant pour transmettre des arguments à vos événements est lorsque vous souhaitez qu'un composant enfant puisse définir une valeur spécifique pour son accessoire. Vous ne voulez jamais modifier directement la valeur d'un accessoire à partir du composant lui-même.
Cependant, heureusement, nous pouvons utiliser des arguments pass avec nos événements personnalisés pour que les valeurs du composant parent changent.
Disons que nous voulons modifier l'événement changeUsername
afin de pouvoir lui transmettre une valeur.
La méthode $emit
prend un deuxième paramètre facultatif pour les arguments. Donc, tout ce que nous faisons est d'ajouter notre nouvelle valeur de nom d'utilisateur après le nom de notre événement.
this.$emit('changeUsername', 'mattmaribojoc')
Ensuite, dans notre composant parent, nous pouvons soit accéder à ces valeurs en ligne en utilisant une variable $event
spéciale, soit écrire une méthode de gestionnaire qui prend un paramètre.
<account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }
3. Utilisation de Vuex pour créer un état partagé au niveau de l'application
D'accord, nous savons comment partager des données entre parents/enfants, mais qu'en est-il des autres composants ? Doit-on créer un système hiérarchique extrêmement complexe si l'on veut transmettre des données ?
Heureusement non. La merveilleuse bibliothèque de gestion d'état Vuex simplifie la vie des développeurs depuis des années. En bref, il crée un magasin de données centralisé accessible par tous les composants.
Dans les méthodes que nous avons utilisées précédemment (props/emitting events), chaque composant a son propre état de données que nous partageons ensuite entre les composants. Cependant, Vuex nous permet d'extraire toutes les données partagées dans un seul état auquel chaque composant peut accéder facilement. Cet état partagé est appelé un magasin.
Essayons.
Étant donné que Vuex est distinct du code principal de Vue, nous devrons d'abord l'installer et l'importer dans notre projet. Tout d'abord, nous devrons exécuter npm install vuex --save
dans notre CLI de projet.
Ensuite, créez un dossier src/store avec un fichier index.js contenant le code suivant.
// store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });
Pour l'inclure dans notre instance racine de Vue, nous devons importer notre fichier store/index.js et le transmettre à notre constructeur Vue.
// main.js import store from "./store"; new Vue({ store, ...
Accéder aux composants Vue Store Inside
Depuis que nous avons ajouté notre magasin Vuex à notre instance Vue racine, il est injecté dans tous les enfants de la racine. Si on veut accéder au store depuis un composant, on peut via this.$store
.
Plongeons maintenant dans les spécificités de chacune des quatre parties d'un magasin Vuec.
1. État
L'état Vuex est un objet qui contient des données au niveau de l'application. Toutes les instances de Vue pourront accéder à ces données.
Pour notre magasin, créons un objet utilisateur qui stocke davantage de données de profil utilisateur.
export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });
Nous pouvons accéder à ces données dans n'importe quel composant d'instance comme celui-ci.
mounted () { console.log(this.$store.state.user.username); },
2. Getters
Nous utilisons les getters Vuex pour renvoyer une valeur modifiée des données d'état. Une bonne façon de penser aux getters est de les traiter comme des propriétés calculées. Par exemple, les getters, comme les propriétés calculées, mettent en cache leurs résultats et ne réévaluent que lorsqu'une dépendance est modifiée.
En nous appuyant sur notre magasin précédent, disons que nous voulons créer une méthode qui renvoie le prénom d'un utilisateur en fonction de l'attribut de nom complet.
getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }
Les propriétés getter de Vuex sont disponibles pour les composants de l'objet store.getters
.
mounted () { console.log(this.$store.getters.firstName); }
Astuce : Connaître les arguments de getter par défaut
Par défaut, les getters Vuex acceptent deux arguments.
- state — l'objet d'état de notre application ;
- getters - l'objet store.getters, ce qui signifie que nous pouvons appeler d'autres getters dans notre magasin.
Chaque getter que vous déclarez nécessitera le premier argument d'état. Et selon la façon dont vous concevez votre code, vos getters peuvent se référencer en utilisant le deuxième argument 'getters'.
Créons un getter de nom de famille qui supprime simplement notre valeur de prénom de notre propriété d'état de nom complet. Cet exemple nécessiterait à la fois les objets state et getters.
lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }
Conseil : Transmettez des arguments personnalisés aux getters Vuex
Une autre fonctionnalité intéressante des getters est que nous pouvons leur transmettre des arguments personnalisés en faisant en sorte que notre getter renvoie une méthode.
prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));
3. Mutations
Les mutations sont le seul moyen de modifier correctement la valeur de l'objet d'état. Un détail important à noter est que les mutations doivent être synchrones .
Comme les getters, les mutations acceptent toujours la propriété d'état Vuex comme premier argument. Ils acceptent également un argument personnalisé - appelé charge utile - comme deuxième argument.
Par exemple, faisons une mutation pour changer le nom d'un utilisateur en une valeur spécifique.
mutations: { changeName (state, payload) { state.user.fullName = payload } },
Ensuite, nous pouvons appeler cette méthode depuis notre composant en utilisant la méthode store.commit
, avec notre charge utile comme deuxième argument.
this.$store.commit("changeName", "New Name");
Le plus souvent, vous voudrez que votre charge utile soit un objet. Non seulement cela signifie que vous pouvez passer plusieurs arguments à une mutation, mais cela rend également votre code plus lisible en raison des noms de propriété dans votre objet.
changeName (state, payload) { state.user.fullName = payload.newName }
Il existe deux manières différentes d'appeler des mutations avec une charge utile.
- Vous pouvez avoir le type de mutation comme premier argument et la charge utile comme second.
- Vous pouvez déclarer passer un seul objet, avec une propriété pour le type et une autre pour la charge utile.
this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });
Il n'y a pas de réelle différence entre la façon dont les deux fonctionnent, c'est donc totalement à la discrétion de chacun. N'oubliez pas qu'il est toujours préférable d'être cohérent tout au long de votre projet, alors quel que soit celui que vous choisissez, respectez-le !
4. Mesures
Dans Vuex, les actions sont assez similaires aux mutations car nous les utilisons pour changer l'état. Cependant, les actions ne modifient pas les valeurs elles-mêmes. Au lieu de cela, les actions commettent des mutations.
De plus, alors que les mutations Vuex doivent être synchrones, les actions ne le sont pas. En utilisant des actions, nous pouvons appeler une mutation après un appel d'API, par exemple.
Alors que la plupart des gestionnaires Vuex que nous avons vus acceptent l'état comme paramètre principal, les actions acceptent un objet de contexte. Cet objet de contexte nous permet d'accéder aux propriétés de notre magasin Vuex (par exemple, état, commit, getters).
Voici un exemple d'action Vuex qui attend deux secondes, puis valide la mutation changeName
.
actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }
Dans nos composants, nous utilisons la méthode store.dispatch afin d'exécuter notre fonction. Nous passons des arguments comme nous l'avons fait avec des mutations. Nous déclarons le type et nous passons tous les arguments personnalisés dans le deuxième argument.
this.$store.dispatch("changeName", { newName: "New Name from Action" });
Emballer
Maintenant, vous devez connaître trois façons différentes de partager des données entre les composants de VueJS : les accessoires, les événements personnalisés et un magasin Vuex.
J'espère que ce didacticiel vous a aidé à mieux comprendre certaines méthodes et meilleures pratiques de Vue. Faites-moi savoir comment vous les avez mis en œuvre dans vos projets !
Lectures complémentaires
Si vous souhaitez approfondir davantage le côté technique/les capacités de chaque technique, voici quelques bons points de départ.
- Site Web du guide officiel de Vuex
- VueJS Docs pour les accessoires et les événements personnalisés
- « WTF, c'est Vuex ? Guide du débutant sur le magasin de données d'application de Vue », Anthony Gore, développeurs de Vue.js