JavaScript: Modernes State Management ohne zusätzliche Library in Vue.js 3

Seite 3: State Management auch für kleine Anwendungen sinnvoll

Inhaltsverzeichnis

Sobald eine Applikation nur um wenige Komponenten wächst, stellt sich die Frage, wie Attribute am besten in mehreren Komponenten verfügbar gemacht werden. Im Anfangsstadium eignet sich dabei die Übergabe von Properties props und das Angeben von Änderungen durch Events $emit. Für eine direkte Eltern-Kind-Beziehung eignet sich das sehr gut. Doch sobald die Properties über mehrere Komponenten jeweils herabgereicht beziehungsweise die Events mehrfach hinaufgereicht werden müssen, wird es schnell unübersichtlich. Es kommt zu einem sogenannten Prop Drilling.

Bereits bei kleinen Applikationen ist es sinnvoll, ein globales State Management einzuführen. Im Vue.js-Universum gibt es dafür die auf das JavaScript-Framework ausgelegte Bibliothek Vuex. Sie bietet ein von der Programmiersprache Elm inspiriertes State Management. Es baut auf den Flux-Prinzipien auf:

  1. Store ist Single Source of Truth
  2. Daten sind readonly
  3. Synchronizität

Diese drei Prinzipien ermöglichen den Aufbau eines globalen State Managements. Jede Vue.js-2-Applikation, die über das Prototypenstadium hinauswächst, profitiert üblicherweise von Vuex.

Die ersten Codebeispiele des Artikels zeigten das Erstellen von Produkten innerhalb einer Single-File Component. Damit stößt man schnell an Grenzen, wenn die Produkte in mehreren Komponenten verfügbar sein sollen. Listing 6 zeigt die Implementierung der Produkte in einem globalen Store:

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    items: [
      {
        id: 0,
        name: "Eggs",
        count: 10
      }
    ]
  },
  mutations: {
    deleteItem(state, id) {
      state.items = state.items.filter((item) => item.id !== id);
    }
  },
  actions: {
    deleteItem(context, id) {
      context.commit("deleteItem", id);
    }
  }
});

Obwohl das Einbinden und Konfigurieren eines Vuex-Stores überraschend einfach funktioniert, gibt es dennoch Stolpersteine beim Verwenden des Tools. Wie so oft in der JavaScript-Welt lassen sich die Dinge auf verschiedene Arten implementieren. Diese Freiheit erschwert oft das Einhalten der Konsistenz in einer Codebase. Anstatt dass Vuex die Arbeit beschleunigt und erleichtert, kann es passieren, dass in Pull Requests über die verschiedenen Vorteile und Nachteile von Hilfsmethoden diskutiert wird.

Es gibt mehrere Möglichkeiten, auf die Funktionen eines Stores zuzugreifen. Es ist oft nicht eindeutig, wann sich welche Methode besser eignet. Diskussionspunkte sind:

  • Zugriff über store.commit bzw. store.dispatch vs. mapMutations bzw. mapActions
  • Aufruf von Mutations nur über eine jeweilige Action vs. direkter Zugriff
  • Zugriff auf den State nur über einen jeweiligen Getter vs. direkter Zugriff

Vergleichbar mit Vue.js-2-Applikationen, lässt sich Vuex in jeder Vue.js-3-Applikation Vuex einsetzen. Vuex ist bereits seit einer Weile auch für Vue.js 3 verfügbar, wie im Migrations Guide zu 4.0 von 3.0 beschrieben.

Es wird allerdings oft übersehen, dass die Single Source of Truth in Vue.js-Applikationen bereits im reaktiven data-Objekt liegt. Jede Instanz einer Komponente greift lediglich auf dessen Proxy zu. Somit ist es bei Vue.js-3-Applikation nicht mehr notwendig, Vuex zu benutzen und die oben genannten Stolpersteine fallen weg.

Seit Vue.js 3 lassen sich ref, reactive und andere Hilfsmethoden in jedem Modul importieren. Dadurch können reaktive Daten in puren JavaScript- oder TypeScript-Dateien erstellt werden. Die Erstellung reaktiver Daten ist somit nicht mehr an das data-Objekt der Options API in einer Single-File Component gebunden.

Die Hilfsmethoden der Reactivity und Composition API ermöglichen in Vue.js 3 State Management ohne fremde Bibliotheken. Die offizielle Dokumentation von Vue.js 3 geht im Speziellen auf das Store Pattern ein. Das Pattern erzeugt einen reaktiven State, basierend auf der Funktion reactive.

Um den mit dem Store Pattern erzeugtem State zwischen mehreren Komponenten zu teilen, gilt es, die reaktiven Daten in ein Composable auszulagern. Ein Composable bezeichnet eine logische Einheit, ausgelagert in eine eigene JavaScript-Datei. Häufig bekommt der Name eines Composables das Prefix use, zum Beispiel useItems. Composables lassen sich in allen Single-File Components importieren und stehen innerhalb der setup()-Funktion zur Verfügung (Listing 7).

import { ref } from "vue";
import { fetchItems } from "../api";

export function useItems() {
  const state = ref({
    items: [
      {
        id: 0,
        name: "Eggs",
        count: 10
      }
    ],
  });

  function deleteItem(id) {
    state.value.items = state.value.items.filter((item) => 
                                             item.id !== id);
  }

  return {
    state,
    deleteItem,
  };
}

In Listing 7 wären die items jedoch in jeder Komponente veränderbar. Es wäre unmöglich, den Überblick zu behalten, an welchen Stellen die Produkte verändert werden.

Es ist an der Zeit, den Blick auf zwei Techniken zu richten, die zeigen, wie lokales und globales State Management in Vue.js 3 aussehen kann. Eine beispielhafte Implementierung befindet sich in der Codesandbox: Locale/globale state management.