Fermer

octobre 8, 2025

Bases de Vue : gestion des états dans Vue

Bases de Vue : gestion des états dans Vue


Découvrez comment faire évoluer la gestion de l’état de Vue avec ref/reactive, props/emits, provide/inject et Pinia à mesure que votre application se développe.

L’état est l’un des premiers concepts de base que vous rencontrerez lors de la création de quelque chose d’interactif avec Vue. Qu’il s’agisse du texte d’un formulaire, des éléments d’un panier ou du profil de l’utilisateur connecté, gérer correctement cet état est essentiel pour que votre application reste stable, réactive et facile à faire évoluer.

Vue 3 a introduit l’API Composition ainsi qu’un système de réactivité remanié, offrant aux développeurs des moyens plus puissants et plus flexibles que jamais pour gérer l’état. Dans ce guide, nous explorerons comment Vue 3 gère l’état, en commençant par l’état local avec ref et reactiveau partage de données avec props et provide/injectet enfin passer au niveau supérieur vers Pinia, la bibliothèque officielle de gestion d’État de Vue. À la fin, vous disposerez d’une feuille de route claire pour choisir l’outil adapté aux besoins de votre application.

Comprendre le concept de gestion d’état dans Vue

L’état est au cœur de chaque application Vue interactive. C’est ce qui rend votre interface utilisateur dynamique : mettez à jour l’état et Vue reflète automatiquement ces modifications dans le DOM.

D’une manière générale, il existe deux types d’États :

  • État local : existe à l’intérieur d’un seul composant (par exemple, un count variable dans un composant compteur)
  • État global : partagé entre plusieurs composants ou sections de votre application (par exemple, le statut d’authentification de l’utilisateur)

Par défaut, chaque composant Vue maintient son propre état réactif, que nous appelons souvent état local. Commençons ici avant d’explorer comment partager l’état entre les composants et éventuellement le gérer globalement avec Pinia.

Etat local avec réf et réactif

Pour gérer l’état réactif local, l’API Vue 3 Composition propose deux outils principaux : ref et reactive. Si vous êtes nouveau sur Vue, décider lequel utiliser peut être déroutant. Décomposons-le donc en nous appuyant sur notre compréhension de l’état local.

Utilisation de la référence

Utiliser ref lorsque vous travaillez avec des valeurs primitives (nombres, chaînes, booléens).

<script setup>
import { ref } from 'vue'

//state
const count = ref(0)

//actions
function increment() {
  count.value++
}
</script>

<!-- view -->
<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

Ici,count est enveloppé dans un refet sa valeur est accessible via .value.

Utiliser réactif

Utiliser reactive lorsque vous travaillez avec des objets ou des tableaux.

<script setup>
import { reactive } from 'vue'

const user = reactive({
  name: ' ',
  age: 25
})
</script>

<template>
   <input v-model="user.name" placeholder="Enter your name" />
  <p>{{ user.name }} is {{ user.age }} years old.</p>
</template>

En coulisses, Vue enveloppe l’objet dans un proxy, afin de pouvoir suivre automatiquement les modifications et mettre à jour le DOM.

L’état local est l’exemple le plus simple du flux de données unidirectionnel de Vue : l’état pilote l’interface utilisateur. Mais une fois que plusieurs composants doivent partager le même morceau d’état, sa gestion locale devient difficile, et c’est à ce moment-là que nous allons au-delà de l’état local.

Partage de l’état entre les composants

L’état local fonctionne bien pour un seul composant, mais que se passe-t-il si plusieurs composants ont besoin des mêmes données ? Vue nous propose plusieurs façons de partager l’état : props/emits et provide/inject.

Accessoires et émissions

Les accessoires permettent à un parent de transmettre un état, tandis que les émissions permettent aux enfants d’envoyer des événements.

Jetons un coup d’œil à cette démo simple ci-dessous :

Composant parent

Vous pouvez transmettre des données du parent à l’enfant via des accessoires.

<!-- Parent.vue -->
<template>
  <Child :count="count" @increment="count++" />
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const count = ref(0)
</script>

Composant enfant

Lorsque l’enfant a besoin de mettre à jour l’état du parent, il peut émettre un événement.

<!-- Child.vue -->
<template>
  <button @click="$emit('increment')">Clicked</button>
</template>

<script setup>
defineProps(['count'])
defineEmits(['increment'])
</script>

Cette approche fonctionne bien pour les petites applications, mais si vous avez des composants profondément imbriqués, le passage des accessoires et des émissions devient rapidement compliqué, ce qui entraîne un autre problème appelé forage d’hélices. Pour éviter cela, Vue propose une option alternative : fournir/injecter.

Fournir/injecter : éviter le forage d’accessoires

Le provide/inject L’API de Vue permet à un composant parent de partager facilement des données avec ses enfants, quelle que soit leur profondeur d’imbrication, sans avoir à transmettre des accessoires à travers chaque couche intermédiaire.

Jetons un coup d’œil à cette démo de code simple ci-dessous :

Composant parent

<!-- ParentComponent.vue -->
<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

   const count = ref(0)
   provide('count', count)

   const increment = () => {
      count.value++
  }
</script>

<template>
  <div>
    <h2>Parent Count: {{ count }}</h2>
    <button @click="increment">Increment</button>
    <!-- Pass down without props -->
    <ChildComponent />
  </div>
</template>

Le provide() La fonction est utilisée dans un composant parent pour rendre les données disponibles à ses descendants. Il faut deux arguments : une clé d’injection et une valeur. La clé peut être une chaîne ou un symbole, et les composants descendants utiliseront cette clé pour accéder à la valeur correspondante via inject(). Un seul composant ne se limite pas à un seul appel ; tu peux appeler provide() plusieurs fois avec des clés différentes pour partager des valeurs différentes.

Le deuxième argument de provide() Ce sont les données que vous souhaitez partager, qui peuvent être de n’importe quel type : primitives, objets, fonctions ou même états réactifs comme ref ou reactive. Lorsque vous fournissez une valeur réactive, Vue ne transmet pas de copie ; il établit une connexion en direct, permettant aux composants descendants qui inject() il reste automatiquement synchronisé avec le fournisseur.

Composant enfant

<!-- ChildComponent.vue -->
<script setup>
import GrandChildComponent from './GrandChildComponent.vue'
</script>
<template>
  <div>
    <h3>I am the Child</h3>
    <!-- Notice: no prop passing -->
    <GrandChildComponent />
  </div>
</template>

Pour injecter des données fournies par un composant ancêtre, utilisez le inject() fonctionner dans le GrandChildComponent.vue:

<!-- GrandChildComponent.vue -->
<script setup>
import { inject } from 'vue'
const count = inject('count')
</script>
<template>
  <div>
    <p>Grandchild sees count: {{ count }}</p>
  </div>
</template>

Dans la démo de code ci-dessus, ParentComponent fournit le réactif countdonc le ChildComponent n’a rien à faire : il transmet simplement l’emplacement/les enfants. Ensuite, le GrandChildComponent peut directement injecter count et restez réactif aux mises à jour des parents

Ceci est une démonstration de base sur la façon d’utiliser le provide/inject modèle; cependant, si vous souhaitez en savoir plus, cet article le couvre en détail : Bases de Vue : Explorer le modèle Provide/Inject de Vue.

Le provide/inject Le motif est parfait pour éviter le perçage d’accessoires dans les applications de taille moyenne. Cependant, à mesure que votre application se développe, la gestion des dépendances de cette manière peut devenir compliquée. Pour les applications plus volumineuses et complexes, nous avons besoin d’une solution de gestion d’état dédiée, plus structurée et évolutive, et c’est là qu’interviennent les bibliothèques comme Pinia.

État à grande échelle avec Pinia

À mesure que votre application se développe, la gestion manuelle de l’état de plusieurs composants se transforme rapidement en un véritable casse-tête. Les modèles que nous avons abordés précédemment fonctionnent bien pour les petits projets, mais une fois que vous créez une application de production à grande échelle, vous devez prendre en compte de nombreux éléments :

  • Remplacement du module à chaud
  • Des conventions plus fortes pour la collaboration en équipe
  • Intégration avec Vue DevTools, y compris la chronologie, l’inspection des composants et le débogage du voyage dans le temps
  • Prise en charge du rendu côté serveur

Vous avez besoin d’un magasin central, d’une source unique de vérité dans laquelle plusieurs composants peuvent lire et écrire, et c’est là que Bœuf entre.

Pinia est la bibliothèque officielle de gestion d’État pour Vue 3 et le successeur de Vuex. Il est conçu pour gérer tous les scénarios répertoriés ci-dessus. C’est beaucoup plus simple, plus intuitif et conçu pour fonctionner de manière transparente avec l’API de composition.

Avant de plonger dans la démo du code, assurons-nous de bien comprendre les principes fondamentaux. À la base, la gestion de l’état concerne l’endroit où se trouvent vos données, la manière dont vous les lisez et la manière de les mettre à jour. Pinia formalise cela avec quatre concepts :

  • État : ce sont vos données réactives réelles. Considérez-le comme la source de vérité pour votre application. Par exemple, les détails du profil d’un utilisateur, l’article dans un panier ou si un modal est ouvert.
  • Store : le conteneur centralisé qui contient votre état. Au lieu de disperser les données sur plusieurs composants, le magasin les centralise, afin que tous les composants puissent y accéder et les mettre à jour sans perçage compliqué d’accessoires.
  • Getters : ceux-ci agissent comme des propriétés calculées pour votre boutique. Ils vous permettent de dériver ou de transformer des valeurs d’état (par exemple, en calculant le coût des articles dans un panier) sans logique de duplication à plusieurs endroits.
  • Actions : fonctions qui mettent à jour votre état. Ce sont comme des méthodes dans un composant Vue, et c’est là que vous conservez la logique pour apporter des modifications, qu’il s’agisse d’incrémenter un compteur, d’ajouter un élément à une liste ou de récupérer des données à partir d’une API.

Pensez-y de cette façon :

  • L’État est ce que vous avez
  • Les getters sont la façon dont vous le voyez
  • Les actions définissent comment cela change
  • Le magasin est l’endroit où tout vit

Montrons donc comment intégrer Pinia à notre application de démonstration de compteur.

Configuration de Pinia

La configuration de Pinia pour une application monopage Vue 3 est simple. Si vous créez un nouveau projet à partir de zéro à l’aide de Vue CLI ou de create-vue, l’assistant de configuration vous demandera même si vous souhaitez utiliser Pinia comme gestion d’état de votre choix.

Pour configurer Pinia manuellement dans un nouveau projet ou l’ajouter à une application existante, suivez ces étapes :

Tout d’abord, installez le package Pinia en utilisant soit npm ou yarn:

npm install pinia

# or

yarn install pinia

Pour enregistrer Pinia dans votre application, ouvrez votre fichier d’entrée (généralement main.js ou main.ts) où l’application est montée, et appelez app.use(pinia) sur votre instance d’application Vue :

main.js


import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

Nous pouvons maintenant passer à la création du magasin pour l’application de démonstration du compteur.

Créer un magasin

Voici un magasin de comptoir simple qui démontre les quatre concepts. Pour créer une boutique, commencez par créer un nouveau fichier contenant son code. Une bonne pratique consiste à placer ces fichiers dans un dossier dédié stores dossier pour garder votre projet organisé.

CounterStore.js

export const useCounterStore = defineStore('counter',  {

----

})

Pour créer une boutique, nous utilisons celui de Pinia defineStore méthode. Il faut deux arguments principaux : le premier est celui du magasin idqui doit être unique dans votre application. Vous pouvez le nommer comme vous le souhaitez, mais pour cet exemple, counter est le plus approprié puisque c’est exactement ce que gère notre magasin.

Le deuxième argument est un objet qui définit les options du magasin. Décomposons ce que nous pouvons y inclure :

État

La première option à définir dans un magasin est state. Si vous avez travaillé avec l’API Options de Vue, cela vous semblera familier. C’est simplement une fonction qui renvoie un objet contenant toutes les données réactives que votre boutique doit gérer. Pour notre démo d’application de compteur, nous ajouterons count propriété à l’État :

export const useCounterStore = defineStore('counter',  {

  state: () => ({
     count: 0,
})

Ensuite, nous pouvons facilement importer ce magasin dans notre CounterButton.vue composant et utiliser le count États.

CounterButton.vue

<script setup>
import { useCounterStore } from '../stores/CounterStore.js’
const counter = useCounterStore()
</script>
<template>
  <div>
  <button >
    Clicked {{ counter.count }} times
 </button>
 
  </div>
</template>

Dans l’exemple de code ci-dessus, nous avons importé le useCounterStore puis appelé la méthode. Cela renverra une copie du magasin de comptoir que nous avons créé précédemment. L’état de ce magasin est global, ce qui signifie que toutes les mises à jour qui y sont apportées seront automatiquement reflétées dans tous les composants qui utilisent le magasin.

Getteurs

Tout comme les propriétés calculées de Vue, les magasins Pinia nous permettent de définir getters. Un getter est essentiellement une valeur calculée dérivée de l’état du magasin. Ils sont utiles lorsque vous souhaitez transformer, filtrer ou calculer quelque chose en fonction de l’état existant sans dupliquer la logique entre les composants. Par exemple, nous pouvons calculer la multiplication de l’état actuel en utilisant la méthode getter :

Mettez à jour votre CounterStore.js avec le code suivant :

export const useCounterStore = defineStore('counter',  {


  state: () => ({
     count: 0,
})


  getters: {
    doubleCount: (state) => state.count * 2
  },

})

C’est tout. Maintenant, nous avons maintenant un doubleCount propriété que nous pouvons utiliser dans n’importe quel composant.

Créer un CounterDispay.vue composant pour utiliser le doubleCount propriété pour afficher un message à l’utilisateur.

<script setup>
import { useCounterStore } from '../stores/CounterStore.js'
const counter = useCounterStore()
</script>

<template>
  <div>
    <p>Current Count: {{ counter.count }}</p>
    <p>Double Count: {{ counter.doubleCount }}</p>
  </div>
</template>

Les getters sont synchrones par conception. Si vous devez effectuer un travail asynchrone (comme récupérer des données), utilisez plutôt une action.

Actes

La dernière option que nous pouvons définir dans notre magasin concerne les actions. Considérez les actions comme la version des méthodes de composants du magasin : elles encapsulent la logique de changement d’état ou d’exécution de tâches. Contrairement aux getters, qui servent uniquement à dériver et à renvoyer des données, les actions sont conçues pour mettre à jour l’état et gérer les effets secondaires.

Un avantage majeur des actions est qu’elles peuvent être asynchrones, contrairement aux getters. Cela les rend idéaux pour des tâches telles que la récupération de données à partir d’une API, la gestion des soumissions de formulaires ou l’exécution de toute opération qui prend du temps avant de renvoyer le résultat dans le magasin. Par exemple, c’est un bon emplacement pour créer une logique pour incrémenter l’état count ou récupérer l’initiale count données de votre API.

Ouvrez notre CounterStore.js et mettez à jour avec le code suivant :

export const useCounterStore = defineStore('counter',  {


  state: () => ({
     count: 0,
})


  getters: {
    doubleCount: (state) => state.count * 2
  },


actions: {
    increment() {
      this.count++
    },

    async fetchInitialCount() {
      const res = await fetch('/api/count')
      const data = await res.json()
      this.count = data.value
    }
  }

  }

})

Maintenant à l’intérieur d’un CounterButton.vue composant, vous pouvez appeler l’action au lieu de muter directement l’état :

<script setup>
import { useCounterStore } from '../stores/CounterStore.js’
const counter = useCounterStore()
</script>

<template>
  <button @click="counter.increment">Increment</button>
  <button @click="counter.fetchInitialCount">Load Initial Count</button>
  <p>Count is: {{ counter.count }}</p>
</template>

D’après la modification du code ci-dessus, le increment() est une action simple qui modifie directement l’état du magasin, tandis que le fetchInitialCount() démontre que l’action gère également les tâches asynchrones, comme les minuteries ou les API. Et comme les magasins Pinia sont réactifs, une fois qu’une action met à jour l’état, tous les composants utilisant ce magasin refléteront instantanément la nouvelle valeur.

Conclusion

La gestion de l’état dans Vue ne doit pas nécessairement être écrasante. Commencez petit avec ref et reactive pour l’État local. Lorsque les composants doivent communiquer, props et emits sont un choix naturel. À mesure que votre application grandit, provide/inject aide à réduire le perçage des accessoires et à garder les choses organisées.

Mais lorsque votre application nécessite un état partagé et cohérent entre de nombreux composants, Pinia se démarque. Il fournit un magasin centralisé et évolutif qui sert de source unique de vérité pour votre application.

Savoir quand utiliser chaque approche est la véritable clé. Vous n’avez pas besoin de Pinia dès le premier jour, mais à mesure que votre projet devient plus complexe, vous apprécierez sa structure et sa fiabilité. Avec ces options en main, vous pouvez gérer l’état en toute confiance, que vous créiez un petit widget ou une application de production à grande échelle.

Si vous souhaitez aller au-delà des bases, le site officiel Communication est la meilleure prochaine étape. Il plonge dans les plugins, les modèles avancés, l’intégration des outils de développement et bien plus encore, le tout expliqué de manière claire et pratique.




Source link