Dies ist der vierte Teil einer fünfteiligen Serie über das JavaScript-Framework Vue.js. In diesem Teil behandeln wir Vuex für die Zustandsverwaltung. Dies ist keine vollständige Anleitung, sondern vielmehr ein Überblick über die Grundlagen, damit Sie loslegen und Vue.js kennenlernen und verstehen können, was das Framework zu bieten hat.

Artikelserie
- Rendering, Direktiven und Ereignisse
- Komponenten, Props und Slots
- Vue-cli
- Vuex (Sie sind hier!)
- Animationen
Vuex
Wenn Sie die letzten Abschnitte über Komponenten und Vue-cli verpasst haben, sollten Sie diese noch einmal durchgehen, bevor Sie weiterlesen. Jetzt, da wir die absoluten Grundlagen über Komponenten und das Übergeben von Zuständen und Props kennen, sprechen wir über Vuex. Es ist ein nützliches Werkzeug für die Zustandsverwaltung.
Zuvor haben wir den Zustand von einer Top-Level-Komponente nach unten weitergegeben, und Geschwisterkomponenten teilten keine Daten. Wenn sie miteinander kommunizieren mussten, mussten wir den Zustand in der Anwendung nach oben verschieben. Das funktioniert! Aber sobald Ihre Anwendung eine gewisse Komplexität erreicht, ergibt dies keinen Sinn mehr. Wenn Sie zuvor mit Redux gearbeitet haben, werden Ihnen all diese Konzepte und die Implementierung vertraut sein. Vuex ist im Grunde die Version von Redux für Vue. Tatsächlich funktioniert Redux auch mit Vue, aber mit Vuex haben Sie den Vorteil, ein Werkzeug zu verwenden, das speziell für Ihr Framework entwickelt wurde.
Zuerst installieren wir Vuex
npm install vuex
oder
yarn add vuex
Ich habe es so eingerichtet: In meinem `/src`-Verzeichnis erstelle ich ein weiteres Verzeichnis namens `store` (dies ist eine Präferenz, Sie könnten auch nur eine Datei `store.js` in demselben Verzeichnis erstellen) und eine Datei namens `store.js`. Die anfängliche Einrichtung in `store.js` würde ungefähr so aussehen (vstore Sublime-Snippet)
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
key: value
}
});
key: value ist ein Platzhalter für jede Art von Zustandsdaten. In anderen Beispielen haben wir counter: 0 verwendet.
In unserer `main.js`-Datei würden wir die folgenden Aktualisierungen vornehmen (aktualisierte Zeilen hervorgehoben)
import Vue from 'vue';
import App from './App.vue';
import { store } from './store/store';
new Vue({
el: '#app',
store: store,
template: '<App/>',
components: { App }
});
Nachdem wir es eingerichtet haben, können wir unsere data() in der Datei als Zustand platzieren, wie wir es zuvor mit Komponenten getan haben, und dann werden wir diesen Zustand entweder verwenden oder mit den folgenden drei Mitteln aktualisieren
- Getter machen Werte statisch in unseren Vorlagen verfügbar. Mit anderen Worten, Getter können den Wert lesen, aber den Zustand nicht verändern.
- Mutationen erlauben uns, den Zustand zu aktualisieren, aber sie sind immer synchron. Mutationen sind der einzige Weg, Daten im Zustand des Speichers zu ändern.
- Aktionen erlauben uns, den Zustand asynchron zu aktualisieren, aber sie verwenden eine bestehende Mutation. Dies kann sehr hilfreich sein, wenn Sie mehrere verschiedene Mutationen auf einmal in einer bestimmten Reihenfolge ausführen müssen.
Manchmal ist es schwierig zu verstehen, warum man mit asynchronen Zustandsänderungen arbeiten sollte, wenn man es noch nicht getan hat. Lassen Sie uns zuerst besprechen, wie das abstrakt geschehen würde, und dann im nächsten Abschnitt zu etwas Realem übergehen. Nehmen wir an, Sie sind Tumblr. Sie haben eine Menge schwerer GIFs auf einer Seite, die lange nicht endet. Sie möchten nur eine bestimmte Menge auf einmal laden, sagen wir 20, bis der Benutzer 200 Pixel vom unteren Rand der ursprünglichen Seite entfernt ist.
Sie könnten eine Mutation haben, die die nächsten 20 anzeigt. Aber Sie haben die nächsten 20 noch nicht, noch wissen Sie, wann Sie das Ende der Seite erreichen. Also erstellen Sie in der App selbst ein Ereignis, das die Scroll-Position überwacht, und lösen eine Aktion aus.
Die Aktion ruft dann die URLs für die nächsten 20 Bilder aus der Datenbank ab und umschließt die Mutation, die die 20 Bilder zum Zustand hinzufügt und sie anzeigt.
Aktionen schaffen im Wesentlichen ein Framework für die Datenanforderung. Sie bieten Ihnen eine konsistente Möglichkeit, die Daten asynchron anzuwenden.
Das grundlegendste abstrakte Beispiel
Im folgenden Beispiel zeigen wir die grundlegendste Implementierung jedes einzelnen Elements, damit Sie ein Gefühl für die Einrichtung und die Funktionsweise bekommen. Payload ist ein optionaler Parameter. Sie können die Menge definieren, um die Sie die Komponente aktualisieren. Machen Sie sich keine Sorgen, wir werden gleich eine tatsächliche Demo verwenden, es ist nur wichtig, zuerst die Grundkonzepte zu verstehen.
In `store.js`
export const store = new Vuex.Store({
state: {
counter: 0
},
//showing things, not mutating state
getters: {
tripleCounter: state => {
return state.counter * 3;
}
},
//mutating the state
//mutations are always synchronous
mutations: {
//showing passed with payload, represented as num
increment: (state, num) => {
state.counter += num;
}
},
//commits the mutation, it's asynchronous
actions: {
// showing passed with payload, represented as asynchNum (an object)
asyncDecrement: ({ commit }, asyncNum) => {
setTimeout(() => {
//the asyncNum objects could also just be static amounts
commit('decrement', asyncNum.by);
}, asyncNum.duration);
}
}
});
Eine wirklich nette Funktion hier ist, dass wir das gesamte Zustandsobjekt in den Mutationen zurückgeben können, aber wir müssen es nicht unbedingt tun, wir können nur das verwenden, was wir brauchen. Time-Travel-Debugging (Durchgehen der Mutationen, um Fehler zu finden) funktioniert trotzdem.
Auf der Komponente selbst würden wir computed für Getter verwenden (dies ist sinnvoll, da der Wert bereits für uns berechnet wurde) und methods mit dispatch, um auf die Mutationen und Aktionen zuzugreifen.
In `app.vue`
computed: {
value() {
return this.$store.getters.value;
}
},
methods: {
increment() {
this.$store.dispatch('increment', 2)
}
}
Oder Sie können einen Spread-Operator verwenden. Ich finde das nützlich, wenn Sie mit vielen Mutationen/Aktionen arbeiten müssen.
export default {
// ...
methods: {
...mapActions([
'increment', // map this.increment() to this.$store.commit('increment')
'decrement',
'asyncIncrement'
])
}
}
Einfaches reales Beispiel
Schauen wir uns die Weather Notifier App noch einmal an, mit einer sehr kleinen und einfachen Menge an Zustand im Vuex-Store. Hier ist das Repository.
Siehe den Pen Vue Weather Notifier von Sarah Drasner (@sdras) auf CodePen.
In `store.js`
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
showWeather: false,
template: 0
},
mutations: {
toggle: state => state.showWeather = !state.showWeather,
updateTemplate: (state) => {
state.showWeather = !state.showWeather;
state.template = (state.template + 1) % 4;
}
}
});
Hier setzen wir den Zustand von showWeather auf `false`, da wir keine Animationen sofort auslösen wollen, nicht bis der Benutzer auf den Telefonknopf drückt. In Mutationen haben wir einen Umschalter für den Zustand von showWeather eingerichtet.
Wir setzen auch die template auf 0 im Zustand. Wir werden diese Zahl verwenden, um nacheinander durch jede der Wetterkomponenten zu wechseln. In Mutationen haben wir also eine Methode namens updateTemplate erstellt. Diese schaltet sowohl den Zustand von showWeather um als auch aktualisiert die template auf die nächste Zahl, aber sie wird wieder bei Null anfangen, wenn sie die Zahl 4 erreicht.
In App.vue
<template>
<div id="app">
...
<g id="phonebutton" @click="updateTemplate" v-if="!showWeather">
...
</g>
<transition
@leave="leaveDroparea"
:css="false">
<g v-if="showWeather">
<app-droparea v-if="template === 1"></app-droparea>
<app-windarea v-else-if="template === 2"></app-windarea>
<app-rainbowarea v-else-if="template === 3"></app-rainbowarea>
<app-tornadoarea v-else></app-tornadoarea>
</g>
</transition>
...
</div>
</template>
<script>
import Dialog from './components/Dialog.vue';
...
export default {
computed: {
showWeather() {
return this.$store.state.showWeather;
},
template() {
return this.$store.state.template;
}
},
methods: {
updateTemplate() {
this.$store.commit('updateTemplate');
}
},
...
components: {
appDialog: Dialog,
...
}
}
</script>
In `dialog.vue`
<script>
export default {
computed: {
template() {
return this.$store.state.template;
}
},
methods: {
toggle() {
this.$store.commit('toggle');
}
},
mounted () {
//enter weather
const tl = new TimelineMax();
...
}
}
</script>
Im obigen Code verwendet App showWeather, um die Vorlage voranzutreiben, während Dialog lediglich die Sichtbarkeit der Komponente umschaltet. Sie sehen auch, dass wir in App.vue verschiedene Kindkomponenten basierend auf dem Wert von `template` im App <template> mit der schicken bedingten Darstellung, die wir im ersten Artikel gelernt haben, ein- und ausblenden. In App hören wir sowohl auf die Zustandsänderungen im Speicher mit den computed-Werten als auch verwenden wir toggle() und updateTemplate() in den Methoden, um die Mutationen des Speichers zu committen.
Dies ist ein einfaches Beispiel, aber Sie können sehen, wie bei einer komplexen App mit vielen Zuständen es hilfreich wäre, den Zustand an einem Ort zu verwalten, anstatt ihn in unseren Komponenten nach oben und unten zu verschieben. Insbesondere wenn Geschwister mit Geschwistern sprechen müssen.
Wenn Sie sich tiefer mit Vuex beschäftigen möchten, gibt es tolle Dokumentation hier. Möglicherweise haben Sie bemerkt, dass wir in der letzten Demo einige <transition>-Komponenten sowie viele Animationen verwendet haben. Lassen Sie uns das als nächstes besprechen!
Artikelserie
- Rendering, Direktiven und Ereignisse
- Komponenten, Props und Slots
- Vue-cli
- Vuex (Sie sind hier!)
- Animationen
Genieße diese Serie. Tolle Demos.
Yup. Hervorragende Serie.
Ebenso!
Hier ist ein großartiger Vortrag vom Autor von Vue