Qu'estce qui va arriver à VueX?
Vuex est la solution pour la gestion des états dans les applications Vue. La prochaine version – Vuex 4 – fait son chemin à travers les dernières étapes avant de sortir officiellement. Cette version apportera une compatibilité totale avec Vue 3, mais n'ajoute pas de nouvelles fonctionnalités. Bien que Vuex ait toujours été une solution puissante et le premier choix de nombreux développeurs pour la gestion de l'état dans Vue, certains développeurs avaient espéré voir plus de problèmes de flux de travail résolus. Cependant, alors même que Vuex 4 sort tout juste de la porte, Kia King Ishii (un membre de l'équipe de base de Vue) parle de ses projets pour Vuex 5 et je suis tellement excité par ce que j'ai vu, je devais le partager avec vous tous. Notez que les plans de Vuex 5 ne sont pas finalisés, donc certaines choses peuvent changer avant la sortie de Vuex 5, mais si cela finit par ressembler à ce que vous voyez dans cet article, cela devrait être une grande amélioration pour le développeur
Avec l'avènement de Vue 3 et de son API de composition les gens ont cherché des alternatives simples construites à la main. Par exemple, You Might Not Need Vuex démontre un modèle relativement simple, mais flexible et robuste pour utiliser l'API de composition avec provide / inject
pour créer des magasins d'état partagés. Comme Gábor le déclare dans son article, cependant, ceci (et d'autres alternatives) ne devraient être utilisés que dans des applications plus petites parce qu'elles manquent de toutes ces choses qui ne concernent pas directement le code: support de la communauté, documentation, conventions, bien Nuxt intégrations et outils de développement.
Ce dernier a toujours été l'un des plus gros problèmes pour moi. L'extension de navigateur Vue devtools a toujours été un outil étonnant pour le débogage et le développement d'applications Vue, et perdre l'inspecteur Vuex avec le «voyage dans le temps» serait une assez grosse perte pour le débogage d'applications non triviales. [19659007] Débogage de Vuex avec Vue Devtools « />
Heureusement, avec Vuex 5, nous pourrons avoir notre gâteau et le manger aussi. Cela fonctionnera plus comme ces alternatives d'API de composition, mais conservera tous les avantages de l'utilisation d'une bibliothèque officielle de gestion d'état. Voyons maintenant ce qui va changer.
Définition d'un magasin
Avant de pouvoir faire quoi que ce soit avec un magasin Vuex, nous devons en définir un. Dans Vuex 4, une définition de magasin ressemblera à ceci:
import {createStore} depuis 'vuex'
export const counterStore = createStore ({
Etat: {
nombre: 0
},
getters: {
double (état) {
retour state.count * 2
}
},
mutations: {
incrément (état) {
state.count ++
}
},
Actions: {
incrément (contexte) {
context.commit ('incrément')
}
}
})
Chaque magasin comprend quatre parties: state
stocke les données, getters
vous donnent l'état calculé, mutations
sont utilisées pour muter l'état, et Les actions
sont les méthodes qui sont appelées de l'extérieur du magasin pour accomplir tout ce qui concerne le magasin. En général, les actions ne commettent pas seulement une mutation, comme le montre cet exemple. Au lieu de cela, ils sont utilisés pour faire des tâches asynchrones parce que les mutations doivent être synchrones ou ils implémentent simplement des fonctionnalités plus compliquées ou en plusieurs étapes. Les actions ne peuvent pas non plus faire muter l'état par elles-mêmes; ils doivent utiliser un mutateur. Alors à quoi ressemble Vuex 5?
import {defineStore} depuis 'vuex'
export const counterStore = defineStore ({
nom: 'compteur',
Etat() {
return {count: 0}
},
getters: {
double () {
renvoyer ce compte * 2
}
},
Actions: {
incrément () {
this.count ++
}
}
})
Il y a quelques changements à noter ici. Tout d'abord, au lieu de createStore
nous utilisons defineStore
. Cette différence est négligeable, mais elle est là pour des raisons sémantiques, que nous reviendrons plus tard. Ensuite, nous devons fournir un nom
pour le magasin, dont nous n'avions pas besoin auparavant. Dans le passé, les modules avaient leur propre nom, mais ils n’étaient pas fournis par le module lui-même; il s'agissait simplement du nom de propriété auquel ils avaient été attribués par le magasin parent qui les avait ajoutés. Maintenant, il n'y a aucun module . Au lieu de cela, chaque module sera un magasin distinct et aura un nom. Ce nom est utilisé par le registre Vuex, dont nous parlerons plus tard.
Après cela, nous devons faire de state
une fonction qui renvoie l'état initial au lieu de simplement le mettre à l'état initial . Ceci est similaire à l'option data
sur les composants. Nous écrivons getters
très similaires à la façon dont nous l'avons fait dans Vuex 4, mais au lieu d'utiliser l'état
comme paramètre pour chaque getter, vous pouvez simplement utiliser this
pour arriver à l'état. De la même manière, les actions
n'ont pas à s'inquiéter du passage d'un objet context
: elles peuvent simplement utiliser this
pour accéder à tout. Enfin, il n'y a pas de mutations
. Au lieu de cela, les mutations sont combinées avec des actions
. Kia a noté que trop souvent, les mutations devenaient simplement de simples setters, les rendant inutilement verbeuses, alors ils les supprimaient. Il n'a pas mentionné s'il était «correct» de muter l'état directement de l'extérieur du magasin, mais nous sommes définitivement autorisés et encouragés à muter l'état directement à partir d'une action et le modèle Flux fronce les sourcils sur la mutation directe de l'état.
Note : Pour ceux qui préfèrent l'API de composition à l'API d'options pour créer des composants, vous serez heureux d'apprendre qu'il existe également un moyen de créer des magasins de la même manière que l'utilisation de l'API de composition .
import {ref, calculé} à partir de 'vue'
importer {defineStore} depuis 'vuex'
export const counterStore = defineStore ('compteur', {
compte const = ref (0)
const double = calculé (() => count.value * 2)
incrément de fonction () {
count.value ++
}
return {count, double, increment}
})
Comme indiqué ci-dessus, le nom est passé comme premier argument de defineStore
. Le reste ressemble à une fonction de composition pour les composants. Cela donnera exactement le même résultat que l'exemple précédent qui utilisait l'API d'options.
Obtenir l'instanciation du magasin
Dans Vuex 4, les choses ont changé par rapport à Vuex 3, mais je vais juste regarder la v4 pour empêcher les choses de devenir incontrôlable. Dans la v4, lorsque vous avez appelé createStore
vous l'avez déjà instancié. Vous pouvez ensuite simplement l'utiliser dans votre application, soit via app.use
ou directement:
import {createApp} depuis 'vue'
import App depuis './App.vue' // Votre composant racine
import store from './store' // La définition de magasin de plus tôt
const app = createApp (application)
app.use (magasin)
app.mount ('# app')
// Maintenant, tous vos composants peuvent y accéder via `this. $ Store`
// Ou vous pouvez utiliser dans les composants de composition avec `useStore ()`
// -----------------------------------------------
// Ou utiliser directement ... c'est généralement déconseillé
importer un magasin à partir de «./store»
store.state.count // -> 0
store.commit ('incrément')
store.dispatch ('incrément')
store.getters.double // -> 4
C'est une chose que Vuex 5 rend un peu plus compliquée que dans la v4. Chaque application peut désormais obtenir une instance distincte de Vuex, ce qui garantit que chaque application peut avoir des instances distinctes des mêmes magasins sans partager de données entre elles. Vous pouvez partager une instance de Vuex si vous souhaitez partager des instances de magasins entre des applications.
import {createApp} depuis 'vue'
importer {createVuex} depuis 'vuex'
import App depuis './App.vue' // Votre composant racine
const app = createApp (application)
const vuex = createVuex () // créer une instance de Vuex
app.use (vuex) // utilise l'instance
app.mount ('# app')
Désormais, tous vos composants ont accès à l'instance Vuex. Au lieu de donner directement la définition de votre (vos) magasin (s), vous les importez ensuite dans les composants dans lesquels vous souhaitez les utiliser et utilisez l'instance Vuex pour les instancier et les enregistrer:
import {defineComponent} from 'vue'
importer un magasin à partir de «./store»
exporter par défaut defineComponent ({
nom: 'App',
calculé: {
compteur () {
renvoyer ceci. $ vuex.store (store)
}
}
})
En appelant $ vuex.store
instancie et enregistre le magasin dans l'instance Vuex. À partir de là, chaque fois que vous utiliserez $ vuex.store
sur ce magasin, il vous rendra le magasin déjà instancié au lieu de l'instancier à nouveau. Vous pouvez appeler la méthode store
directement sur une instance de Vuex créée par createVuex ()
.
Maintenant, votre boutique est accessible sur ce composant via this.counter
]. Si vous utilisez l'API de composition pour votre composant, vous pouvez utiliser useStore
au lieu de this. $ Vuex.store
:
import {defineComponent} from 'vue'
importer {useStore} depuis 'vuex' // importer useStore
importer un magasin à partir de «./store»
exporter par défaut defineComponent ({
installer () {
compteur const = useStore (magasin)
return {counter}
}
})
Il y a des avantages et des inconvénients à importer le magasin directement dans le composant et à l'instancier à cet endroit. Il vous permet de diviser le code et de charger paresseusement le magasin uniquement là où il est nécessaire, mais il s'agit désormais d'une dépendance directe au lieu d'être injecté par un parent (sans oublier que vous devez l'importer à chaque fois que vous souhaitez l'utiliser). Si vous souhaitez utiliser l'injection de dépendances pour la fournir dans toute l'application, en particulier si vous savez qu'elle sera utilisée à la racine de l'application où le fractionnement de code ne vous aidera pas, vous pouvez simplement utiliser provide
:
importer {createApp} depuis 'vue'
importer {createVuex} depuis 'vuex'
importer l'application depuis './App.vue'
importer un magasin à partir de «./store»
const app = createApp (application)
const vuex = createVuex ()
app.use (vuex)
app.provide ('store', store) // fournit le magasin à tous les composants
app.mount ('# app')
Et vous pouvez simplement l'injecter dans n'importe quel composant où vous allez l'utiliser:
import {defineComponent} from 'vue'
exporter par défaut defineComponent ({
nom: 'App',
injecter: ['store']
})
// Ou avec l'API de composition
import {defineComponent, inject} depuis 'vue'
exporter par défaut defineComponent ({
installer () {
const store = inject ('store')
retour {store}
}
})
Je ne suis pas enthousiasmé par cette verbosité supplémentaire, mais elle est plus explicite et plus flexible, ce dont je suis fan. Ce type de code est généralement écrit une fois tout de suite au début du projet, puis cela ne vous dérange plus, bien que maintenant vous deviez soit fournir chaque nouveau magasin, soit l'importer chaque fois que vous souhaitez l'utiliser, mais importer ou injecter des modules de code est la façon dont nous devons généralement travailler avec autre chose, donc il s'agit simplement de faire fonctionner Vuex davantage en fonction de la façon dont les gens ont déjà tendance à travailler.
Utilisation d'un magasin
En plus d'être un fan du la flexibilité et la nouvelle façon de définir les magasins de la même manière qu'un composant utilisant l'API de composition, il y a encore une chose qui me rend plus excité que tout le reste: comment les magasins sont utilisés. Voici à quoi ressemble l'utilisation d'un magasin dans Vuex 4.
store.state.count // État d'accès
store.getters.double // Accéder aux Getters
store.commit ('increment') // État de la mutation
store.dispatch ('increment') // Exécuter des actions
State
getters
mutations
et actions
sont toutes gérées de différentes manières via différentes propriétés ou méthodes. Cela a l’avantage d’expliciter, ce que j’ai loué plus tôt, mais cette explicitation ne nous rapporte vraiment rien. Et cette API ne devient plus difficile à utiliser que lorsque vous utilisez des modules d'espacement de noms. En comparaison, Vuex 5 semble fonctionner exactement comme vous le souhaiteriez normalement:
store.count // Access State
store.double // Accéder aux Getters (transparent)
store.increment () // Exécuter des actions
// No Mutators
Tout – l'état, les getters et les actions – est disponible directement à la racine du magasin, ce qui le rend simple à utiliser avec beaucoup moins de verbosité et supprime pratiquement tout le besoin d'utiliser mapState
, mapGetters
mapActions
et mapMutations
pour l'API d'options ou pour écrire des instructions extra calculées
ou des fonctions simples pour l'API de composition . Cela donne simplement à un magasin Vuex l'aspect et le comportement d'un magasin normal que vous construiriez vous-même, mais il obtient tous les avantages des plugins, des outils de débogage, de la documentation officielle, etc.
Composing Stores
Le dernier aspect de Vuex 5 que nous allons examiner aujourd'hui est la composabilité. Vuex 5 ne dispose pas de modules d'espacement de noms qui sont tous accessibles à partir du magasin unique. Chacun de ces modules serait divisé en un magasin complètement séparé. C'est assez simple à gérer pour les composants: ils importent simplement les magasins dont ils ont besoin, les activent et les utilisent. Mais que faire si un magasin souhaite interagir avec un autre magasin? Dans la v4, l'espacement des noms complique le tout, vous devez donc utiliser l'espace de noms dans vos appels commit
et dispatch
utilisez rootGetters
et rootState
puis remontez dans les espaces de noms auxquels vous souhaitez accéder aux getters et à l'état. Voici comment cela fonctionne dans Vuex 5:
// store / greeter.js
importer {defineStore} depuis 'vuex'
exporter par défaut defineStore ({
nom: 'greeter',
Etat () {
return {salutation: 'Bonjour'}
}
})
// store / counter.js
importer {defineStore} depuis 'vuex'
import greeterStore depuis './greeter' // Importez le magasin avec lequel vous souhaitez interagir
exporter par défaut defineStore ({
nom: 'compteur',
// Puis `utilise` le magasin
utilisation () {
return {greeter: greeterStore}
},
Etat () {
return {count: 0}
},
getters: {
GreetCount () {
return `$ {this.greeter.greeting} $ {this.count} '// y accéder depuis this.greeter
}
}
})
Avec la v5, nous importons le magasin que nous souhaitons utiliser, puis l'enregistrons avec use
et maintenant il est accessible partout dans le magasin, quel que soit le nom de propriété que vous lui avez donné. Les choses sont encore plus simples si vous utilisez la variante d'API de composition de la définition de magasin:
// store / counter.js
import {ref, calculé} à partir de 'vue'
importer {defineStore} depuis 'vuex'
import greeterStore depuis './greeter' // Importez le magasin avec lequel vous souhaitez interagir
export default defineStore ('counter', ({use}) => {// `use` est passé à la fonction
const greeter = use (greeterStore) // utilisez `use` et maintenant vous avez un accès complet
compte const = 0
const GreetCount = calculé (() => {
return `$ {greeter.greeting} $ {this.count}` // y accéder comme n'importe quelle autre variable
})
return {count, GreetCount}
})
Plus de modules d'espacement de noms. Chaque magasin est séparé et est utilisé séparément. Vous pouvez utiliser utiliser
pour rendre un magasin disponible dans un autre magasin pour les composer. Dans les deux exemples, use
est essentiellement le même mécanisme que vuex.store
de plus tôt et ils garantissent que nous instancions les magasins avec l'instance correcte de Vuex.
TypeScript Support [19659011] Pour les utilisateurs de TypeScript, l'un des plus grands aspects de Vuex 5 est que la simplification a rendu plus simple l'ajout de types à tout. Les couches d'abstraction que les anciennes versions de Vuex avaient rendues presque impossible et à l'heure actuelle, avec Vuex 4, elles ont augmenté notre capacité à utiliser des types, mais il y a encore trop de travail manuel pour obtenir une quantité décente de support de type, alors que dans la v5 , vous pouvez mettre vos types en ligne, comme vous l'espérez et vous vous y attendez.
Conclusion
Vuex 5 semble être presque exactement ce que j'espérais – et probablement beaucoup d'autres -, et je pense qu'il ne peut pas viens assez tôt. Cela simplifie la plupart de Vuex, en supprimant une partie de la surcharge mentale impliquée, et ne devient plus compliqué ou verbeux que là où cela ajoute de la flexibilité. Laissez des commentaires ci-dessous sur ce que vous pensez de ces changements et quels changements vous pourriez apporter à la place ou en plus. Ou allez directement à la source et ajoutez une RFC (Request for Comments) à la liste pour voir ce que pense l'équipe de base.

Source link