Zum Inhalt
wannacode

Zustand

Kurz gesagt: Zustand ist ein Wert, den sich eine Komponente zwischen zwei Renderings merkt; in Vue wird er mit ref gehalten, und jede Änderung aktualisiert die Komponente auf der Seite.

Problem

Der Button zählt nicht

Die Produktkarte hat einen Button „Hinzufügen“ und einen Zähler „Im Warenkorb“. Klick dreimal auf „Hinzufügen“.

Vue

Die Konsole sagt count = 1, 2, 3. Die Karte sagt 0. Es gibt keinen Fehler. Die Variable count ändert sich, aber der Bildschirm folgt ihr nicht. Wo liegt der Unterschied?

Die Idee

Eine einfache Variable reicht nicht

Die Karte zeichnet nicht dein Code, sondern die Bibliothek. Sie zeichnet nur neu, wenn sie weiß, dass sich etwas geändert hat. count++ ändert die Variable, sagt es aber niemandem.

<script setup> läuft einmal für jede Karte, und das Template führt Vue aus. Vue führt das Template nur dann erneut aus, wenn sich etwas ändert, das es beobachtet. Eine einfache let-Variable beobachtet Vue nicht. Der Klick ändert count, aber das Template läuft nicht erneut, also bleibt die Karte bei 0.

Du brauchst Daten, die sich die Komponente zwischen zwei Renderings merkt und deren Änderung sie neu zeichnet. Das heißt Zustand (State).

Die Idee

useState und ref

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add() {
count.value++;
}
</script>
<template>
<p>Im Warenkorb: {{ count }}</p>
</template>
  • ref(0) gibt eine Box zurück, und die Zahl steckt in count.value. Im Skript schreibst du .value.
  • Im Template packt Vue die Box aus: Du schreibst {{ count }}, ohne .value.
  • Liest das Template count, merkt sich Vue das. Ändert sich count.value, führt Vue das Template erneut aus.

Den Klick-Handler gibst du als Funktion an: @click="add". In Vue funktionieren auch @click="add()" oder @click="count++": Das Template packt sie in eine Funktion, die beim Klick läuft.

Vue

Sieh es dir an

Sieh, was bei einem Klick passiert

<script setup>
const count = ref(0);
function addThree() {
count.value++;
count.value++;
count.value++;
}
</script>
<template>
<article>
<button @click="addThree">+3</button>
<p>Im Warenkorb: {{ count }}</p>
</article>
</template>
Schritt für Schritt, in Worten
  1. Die Karte hält count in einer Ref: einer Box, deren Wert count.value ist. Vue beobachtet, wer sie liest.
  2. Vue führt das Template aus. Es liest count und gibt eine Beschreibung zurück: einen Button und einen Text mit 0. Vue merkt sich, dass das Template count liest.
  3. Beim ersten Mal wird die Beschreibung zu echten Elementen im DOM. Die Seite zeigt jetzt „Im Warenkorb: 0“.
  4. Ein Klick auf +3: Der Browser ruft addThree auf.
  5. Dreimal count.value++. Die Box ändert sich sofort: 1, 2, 3. Vue plant, das Template erneut auszuführen, aber nur einmal.
  6. Der Handler ist fertig. Vue führt das Template einmal aus, mit count = 3, und es gibt eine neue Beschreibung zurück.
  7. Vue vergleicht die neue Beschreibung mit der alten. Der Button ist derselbe, nur der Text hat sich geändert. Im DOM ändert sich nur der Text.
  8. Der Zähler zeigt 3. Die Änderung einer Ref ist sofort da, und das Zeichnen passiert einmal, nach dem Handler.

Die Karte wird nicht neu gebaut. Die Bibliothek vergleicht die neue Beschreibung mit der alten und ändert im DOM nur den Unterschied: den Text. Der Button ist dasselbe Element, und der Fokus bleibt auf ihm. Das ist der Unterschied zu render() im DOM-Modul, das alles ersetzt hat.

Probier es aus

Repariere den Zähler

Das ist die Karte vom Anfang. Mach count zum Zustand, damit die Karte nach jedem Klick auf „Hinzufügen“ die neue Zahl zeigt.

Vue

Die Idee

Die Box und die Zahl

Mineralwasser gibt es auch in einer Packung mit 6 Flaschen. Praktisch wäre, wenn der Packungs-Button sechsmal add() aufruft. Hier verhalten sich die beiden Bibliotheken unterschiedlich.

count.value ist immer die aktuelle Zahl, also ergibt sechsmal count.value++ den Wert 6. Das Template läuft dafür nicht sechsmal. Vue sammelt die Änderungen und führt es einmal aus, nach dem Handler.

Der Fehler ist hier ein anderer. Die Box ist count, und count.value ist eine Zahl. Eine Zahl, die in eine andere Variable gelegt wird, ist eine Kopie:

let bottles = count.value;
bottles += 6; // ändert die Kopie, count bleibt gleich

Vue beobachtet die Box, nicht jede Variable, in der die Zahl schon war. Schreib in die Box: count.value += 6.

Probier es aus

Finde den Fehler

„Packung hinzufügen“ soll 6 Flaschen hinzufügen. Welche Zeile ist schuld?

Wähle die Zeile mit dem Fehler.

Probier es aus

Eine Packung Wasser

Schreibe addBox() so, dass „Packung hinzufügen (6 Stk.)“ 6 Flaschen zu dem addiert, was schon im Warenkorb ist. Die Prüfung klickt einmal auf „Hinzufügen“, dann zweimal auf die Packung und erwartet 1, 7 und 13.

Vue

Die Idee

Jede Karte merkt sich ihren eigenen Wert

Der Zustand gehört zur Instanz, nicht zum Code. Der Katalog hat zwei Karten aus derselben ProductCard, und jede hat ihr eigenes count:

Vue

Klick zweimal auf „Hinzufügen“ beim Brot und einmal bei der Milch: 2 und 1. Die Bibliothek hält den Zustand jeder Karte nach ihrem Platz im Baum der Komponenten. Nimmst du die Karte von der Seite, geht ihr Zustand mit. Wenn zwei Karten dieselbe Zahl zeigen müssen, muss sie in ihrem gemeinsamen Elternelement leben. Darum geht es in der Lektion über gemeinsamen Zustand in diesem Modul.

Genauer, für Neugierige

ref und reactive. reactive({ count: 0 }) macht das Objekt beobachtet und hat kein .value, funktioniert aber nur mit Objekten, und das ganze Objekt lässt sich nicht ersetzen. ref funktioniert mit jedem Wert, deshalb empfiehlt die Vue-Dokumentation es als Standard.

Wann das Template läuft. Vue führt das Template im nächsten Microtask erneut aus, wenn der Code, der gerade läuft, fertig ist. await nextTick() wartet auf dieses Rendering, zum Beispiel um das neue DOM zu lesen.

Auspacken. Im Template werden Refs auf der obersten Ebene von <script setup> ausgepackt. Eine Ref in einem einfachen Objekt ({ total: ref(0) }) wird in einem Ausdruck wie {{ cart.total + 1 }} nicht ausgepackt.

Options API. Älterer Vue-Code schreibt die Komponente als Objekt mit data(), methods und this.count. Neuer Code nutzt <script setup> und die Composition API.

Übung

Wähle die Menge

Der Katalog hat drei Karten. Füge in jeder eine Menge hinzu, die bei 1 beginnt, einen Button „−“ (Name für den Screenreader: „Weniger Weißbrot“), einen Button „+“ („Mehr Weißbrot“) und eine Zeile „2 × 1,20 € = 2,40 €“ mit Menge, Preis und Summe. „−“ ist deaktiviert, wenn die Menge 1 ist. Jede Karte zählt für sich.

Vue

Test

Teste dich

1. Warum ändern let count = 0 und count++ in der Komponente nichts am Bildschirm?
2. Wo schreibst du bei const count = ref(0) das .value?
3. count.value ist 0. Der Handler macht dreimal count.value++. Was passiert?
4. Was macht let bottles = count.value; bottles += 6;?
5. Die Seite hat zwei ProductCards, jede mit ihrem eigenen count. Die Kundin drückt in der ersten auf „Hinzufügen“. Was passiert?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·