Bonjour, Habr! Je vous présente la traduction de l'article «Maîtriser Vuex - Zero to Hero» de Sanath Kumar.
La documentation officielle de Vuex le définit comme un modèle de gestion d'état + bibliothèque pour les applications Vue.js. Mais qu'est-ce que cela signifie? Qu'est-ce qu'un modèle de gestion d'état?
Imaginez que vous travaillez sur une grande application Web avec des centaines de routes et de composants. Ne serait-il pas plus facile de stocker toutes les données dont nous aurions besoin dans une application dans un stockage centralisé?

Chaque composant ou route de notre application demandera des données à l'état Vuex et retransférera les données modifiées à l'état.
En substance, l'état de Vuex peut être considéré comme la seule source de vérité pour l'ensemble de l'application.
Les données sont stockées dans l'état en tant qu'objet JSON. Par exemple:
state: { name: "John Doe", age: "28" }
Mais comment nos composants et nos routes peuvent-ils accéder aux données stockées dans notre état? Pour ce faire, nous devons définir des getters à l' intérieur de notre référentiel Vuex qui retourneront les données du référentiel à nos composants. Voyons à quoi ressemble un getter simple, qui obtient le nom de notre référentiel:
getters: { NAME: state => { return state.name; }, }
Notez que le nom du getter est en majuscules. Ce n'est qu'une recommandation de style de code. Il n'est pas nécessaire de le suivre si vous ne l'aimez pas.
Maintenant que nous avons défini un getter pour le nom, il est incroyablement facile d'obtenir la valeur du nom dans notre composant. Le code ci-dessous vous permet de le faire.
let name = this.$store.getters.NAME;
Nous avons compris comment obtenir des données du stockage. Voyons maintenant comment définir les données dans le référentiel. Nous définirons les setters, non? De plus, les setters Vuex sont nommés un peu différemment. Nous définissons une mutation pour définir les données à notre état Vuex.
mutations: { SET_NAME: (state, payload) => { state.name = payload; }, }
Qu'est-ce que la charge utile? La charge utile est les données transmises à notre mutation à partir du composant qui fait la mutation. Comment pouvons-nous faire cela? Très simple:
this.$store.commit('SET_NAME', your_name);
Ce morceau de code changera l'état de l'application et définira toute valeur affectée à votre_nom pour la propriété name dans notre référentiel.
MUTATIONS SYNCHRONES
Imaginez que nous ayons une liste de noms stockée dans une base de données sur un serveur distant. Le serveur nous fournit un point de terminaison qui renvoie un tableau de noms qui peuvent être utilisés dans notre Vue.js. Bien sûr, nous pouvons utiliser Axios pour interroger le point de terminaison et obtenir les données.
let {data} = await Axios.get('https://myapiendpoint.com/api/names');
Après cela, nous pouvons passer le tableau retourné à notre état Vuex du magasin à l'aide d'une mutation. Facile, non? Mais pas vraiment. Les mutations sont synchrones et nous ne pouvons pas exécuter d'opérations asynchrones, telles que des appels API, à l'intérieur d'une mutation.
Que devons-nous faire alors? Créez des actions .
Les actions sont comme des mutations, mais au lieu de changer directement l'état, elles font une mutation. Cela vous semble confus? Regardons l'annonce de l'action.
actions: { SET_NAME: (context, payload) { context.commit('SET_NAME', payload); }, }
Nous avons défini une action appelée SET_NAME qui prend le contexte et la charge utile comme paramètres. L'action valide la mutation SET_NAME, créée précédemment, avec les données qui lui sont transmises, c'est-à-dire votre_nom .
Maintenant, au lieu d'invoquer directement la mutation, nos composants déclenchent l'action SET_NAME avec un nouveau nom en tant que données comme suit:
this.$store.dispatch('SET_NAME', your_name);
Ensuite, l'action initie la mutation avec les données qui lui sont transmises, c'est-à-dire votre_nom .
Mais pourquoi?
Vous vous demandez peut-être pourquoi une déclaration d'action est requise si nous pouvons simplement initier des mutations avec une nouvelle valeur directement à partir de nos composants. Comme mentionné ci-dessus, les mutations sont synchrones, mais aucune action.
Dans l'exemple ci-dessus, le cas est considéré lorsque vous devez mettre à jour la valeur du nom, mais pas seulement dans son état, mais également dans la base de données s'exécutant sur le serveur distant. Je suis sûr que c'est ainsi que vous comptez utiliser Vuex dans un vrai projet dans 99% des cas. Jetez un œil à l'extrait de code suivant:
mutations: { SET_NAME: (state, name) => { state.name = name; }, }, actions: { SET_NAME: async (context, name) => { let {data} = await Axios.post('http://myapiendpoint.com/api/name', {name: name}); if (data.status == 200) { context.commit('SET_NAME', name); } }, }
Le code lui-même est explicite. Nous utilisons Axios pour envoyer le nom au point de terminaison. Si la demande POST a réussi et que la valeur du nom de champ a été modifiée avec succès sur le serveur, nous initialisons la mutation SET_ NAME pour mettre à jour la valeur du nom dans notre état.
N'UTILISEZ JAMAIS DES MUTATIONS DIRECTEMENT. POUR CETTE UTILISATION TOUJOURS DES ACTIONS.
Configuration du stockage Vuex dans Vue.JS
Approfondissons et découvrons comment implémenter Vuex dans une application réelle.
Étape 1. Installez Vuex
npm install --save vuex
Étape 2. Création d'un référentiel Vuex
- Créez le répertoire du magasin à la racine de notre application.
- Créez le fichier index.js dans ce répertoire et utilisez le code ci-dessous pour créer un nouveau référentiel.
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export const store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, });
Étape 3. Ajout de stockage Vuex à l'application Vue.JS
1. Importez le référentiel dans le fichier main.js:
import {store} from './store';
2. Ajoutez du stockage à l'instance Vue, comme indiqué ci-dessous:
new Vue({ el: '#app', store, router, render: h => h(App), });
Nous pouvons maintenant ajouter des variables d'état, des getters, des mutations et des actions à notre référentiel Vuex.
Exemple
Jetez un œil au référentiel Vuex d'une application de liste de tâches simple. "Pas seulement une autre liste de choses à faire !!!". Hein? Ne t'inquiète pas. À la fin de cet article, vous apprendrez à utiliser la pleine puissance et la puissance de Vuex.
import Vue from 'vue'; import Vuex from 'vuex'; import Axios from 'axios'; Vue.use(Vuex); export const store = new Vuex.Store({ state: { todos: null, }, getters: { TODOS: state => { return state.todos; }, }, mutations: { SET_TODO: (state, payload) => { state.todos = payload; }, ADD_TODO: (state, payload) => { state.todos.push(payload); }, }, actions: { GET_TODO: async (context, payload) => { let {data} = await Axios.get('http://yourwebsite.com/api/todo'); context.commit('SET_TODO', data); }, SAVE_TODO: async (context, payload) => { let {data} = await Axios.post('http://yourwebsite.com/api/todo'); context.commit('ADD_TODO', payload); }, }, });
Ajouter un nouvel élément à la liste des tâches
Dans votre composant, lancez l'action SAVE_TODO en lui passant un nouvel élément à faire, comme indiqué dans l'extrait de code ci-dessous.
let item = 'Get groceries'; this.$store.dispatch('SAVE_TODO', item);
L'action SAVE_TODO envoie une requête POST au point de terminaison, puis initie la mutation ADD_TODO , qui ajoute une tâche à faire à la variable d'état todos .
Objets à faire
À l'intérieur du bloc monté () de votre composant, lancez la deuxième action GET_TODO , qui reçoit tous les éléments à faire du point de terminaison et les stocke dans la variable d'état todos , initiant la mutation SET_TODO:
mounted() { this.$store.dispatch('GET_TODO'); }
Accès aux tâches à faire dans un composant
Pour accéder à l'élément todos à l' intérieur d'un composant, créez une propriété calculée:
computed: { todoList() { return this.$store.getters.TODOS; }, }
À l'intérieur du composant, vous pouvez accéder à la propriété calculée:
<div class="todo-item" v-for="item in todoList"></div>
Utilisation de la méthode mapGetters
Il existe un moyen encore plus simple d'accéder aux tâches à effectuer dans un composant en utilisant la méthode mapGetters fournie par Vuex.
import {mapGetters} from 'vuex'; computed : { ...mapGetters(['TODOS']), // }
Vous avez peut-être déjà deviné que le code à l'intérieur du modèle doit être modifié, comme indiqué dans l'extrait ci-dessous.
<div class="todo-item" v-for="item in TODOS"></div>
Remarquez comment nous avons utilisé l'opérateur de distribution ES6 [...] dans nos propriétés calculées.
LE STOCKAGE VUEX N'EST PAS JUSTE LA SOURCE DE L'ÉTAT ACTUEL DE VOTRE APPLICATION. C'EST AUSSI LE SEUL POINT QUI DEVRAIT CHANGER CET ÉTAT.
Cela nécessite une petite explication. Nous avons déjà appris à créer des actions pour recevoir et installer des tâches dans notre référentiel. Et si nous devons mettre à jour un élément et le marquer? Où exécutons-nous le code pour cela?
Sur Internet, vous pouvez trouver différentes opinions à ce sujet. La documentation manque également de directives claires à ce sujet.
Je recommanderais de stocker tous les appels d'API dans des actions dans votre référentiel Vuex. Ainsi, chaque changement d'état se produit uniquement à l'intérieur du référentiel, ce qui facilite le débogage et simplifie la compréhension du code, et facilite également l'édition du code.
Organisation du code
L'enregistrement de toutes les variables d'état, des getters, des actions et des mutations dans un seul fichier le rendra rapidement encombrant dès que vous commencerez à travailler avec de grandes applications. Voyons comment vous pouvez organiser le stockage dans plusieurs fichiers sous forme de modules.
Créez un nouveau répertoire dans votre référentiel et nommez-le modules . Ajoutez le fichier todos.js au répertoire créé contenant le code suivant:
const state = {}; const getters = {}; const mutations = {}; const actions = {}; export default { state, getters, mutations, actions, };
Nous pouvons maintenant déplacer les variables d'état, les getters, les mutations et les actions du fichier index.js vers le fichier todos.js . N'oubliez pas d'importer Axios . Tout ce que nous devons faire est de faire savoir à Vuex que nous avons créé le module de stockage et où le trouver. Le fichier index.js mis à jour devrait ressembler à ceci:
import Vue from 'vue'; import Vuex from 'vuex'; import Axios from 'axios'; import todos from './modules/todos'; Vue.use(Vuex); export const store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: { todos, }, });
Le fichier todos.js ressemblera à ceci:
import Axios from 'axios'; state = { todos: null, }; getters = { TODOS: state => { return state.todos; }, }; mutations = { SET_TODO: (state, payload) => { state.todos = payload; }, ADD_TODO: (state, payload) => { state.todos.push(payload); }, }; actions = { GET_TODO: async (context, payload) => { let {data} = await Axios.get('http://yourwebsite.com/api/todo'); context.commit('SET_TODO', data); }, SAVE_TODO: async (context, payload) => { let {data} = await Axios.post('http://yourwebsite.com/api/todo'); context.commit('ADD_TODO', payload); }, }; export default { state, getters, mutations, actions, };
Résumé
- L'état de l'application est stocké sous la forme d'un seul grand objet JSON.
- Les getters sont utilisés pour accéder aux valeurs stockées dans le magasin.
- Les mutations mettent à jour votre condition. Il ne faut pas oublier que les mutations sont synchrones.
- Toutes les opérations asynchrones doivent être effectuées dans les actions . Les actions changent d'état et déclenchent des mutations.
- Faites-en une règle pour initier des mutations exclusivement par l' action .
- Les modules peuvent être utilisés pour organiser votre stockage en plusieurs petits fichiers.
Vuex rend le travail avec Vue beaucoup plus facile et plus amusant. Si vous êtes débutant, il peut y avoir des situations où il est difficile pour vous de décider d'utiliser Vuex dans certains domaines de votre application. Suivez votre instinct. Vous atteindrez la grande vitesse assez rapidement.