Zum Inhalt
wannacode

Objekte und Arrays aktualisieren

Kurz gesagt: Ein Objekt oder Array im Vue-Zustand wird an Ort und Stelle geändert, weil Vue Änderungen über einen Proxy beobachtet; ein neues Array in .value funktioniert auch, ein einfaches Array ohne ref aber nicht.

Problem

„Entfernen“ entfernt nichts

Der Warenkorb hat drei Zeilen, jede mit einem Button „Entfernen“. Klick bei dem Brot auf „Entfernen“.

Vue

Die Konsole sagt Zeilen: 2. Der Bildschirm zeigt alle drei Zeilen und die alte Summe. Die Daten haben sich geändert, der Bildschirm folgt ihnen nicht, und es gibt keinen Fehler.

remove legt ein neues Array ohne das Brot in cart. Der neue Wert steht in der Variablen. Warum zeichnet Vue nicht?

Die Idee

Wie die Bibliothek eine Änderung sieht

Bei einer Zahl ist es klar: 1 und 2 sind verschieden. Ein Array und ein Objekt sind dagegen Referenzen: Die Variable zeigt auf ein Objekt im Speicher. Es gibt zwei Fragen. Zeigt die Variable auf ein anderes Objekt? Und hat sich der Inhalt desselben Objekts geändert? Jede Bibliothek fragt auf ihre Art, und hier zeigt sich der Unterschied im Code.

Vue stellt die zweite Frage. ref([...]) packt das Array in einen Proxy: ein Objekt, das vor dem Array steht und jedes Lesen und Schreiben sieht. push, splice und line.qty++ gehen durch den Proxy. Er hält die Änderung fest und sagt dem Template, das das Array liest, dass es erneut laufen soll. Deshalb ändert man in Vue das Array an Ort und Stelle, und das ist der normale Weg.

Der Proxy sieht aber die Variable nicht, die auf ihn zeigt. Im Beispiel let cart = reactive([...]) und dann cart = cart.filter(...): Die Variable zeigt jetzt auf ein neues, einfaches Array. Das Template liest das alte Array über den alten Proxy, und niemand sagt es ihm. Bei const cart = ref([...]) kommt das neue Array nach cart.value, und die Ref sieht diese Änderung.

Sieh es dir an

Sieh: dasselbe Array oder ein neues

<script setup>
const cart = ref([bread]);
function addMilk() {
cart.value.push(milk);
}
function addCheese() {
cart.value = [...cart.value, cheese];
}
</script>
Schritt für Schritt, in Worten
  1. cart ist eine Ref. Darin steckt das Array, in einen Proxy gepackt: ein Objekt, das vor dem Array steht und jedes Lesen und Schreiben sieht.
  2. cart.value.push(milk) geht durch den Proxy. Der Proxy hält fest: Das Array hat sich geändert.
  3. Der Proxy sagt dem Template, das cart liest, dass es erneut laufen muss.
  4. Das Template läuft, und der Bildschirm zeigt beide Zeilen.
  5. Auch das geht: ein neues Array in cart.value. Die Ref bemerkt die Änderung von .value.
  6. Die Ref sagt es dem Template, und das Template läuft mit dem neuen Array erneut.
  7. Der Bildschirm zeigt drei Zeilen. In Vue funktionieren beide Wege.
  8. Die Regel in Vue: Ändere über die Ref. Eine einfache Variable oder die Kopie einer Zahl sieht Vue nicht.
Beide Wege gehen durch die Ref: push durch den Proxy des Arrays und das neue Array durch .value. Vue übersieht nur das, was an beiden vorbeigeht.

Die Idee

Eine Änderung durch den Proxy

Der Warenkorb hat drei Aktionen: eine Zeile hinzufügen, die Menge einer Zeile ändern, eine Zeile entfernen.

// hinzufügen
cart.value.push({ ...product, qty: 1 });
// eine Zeile ändern: Auch die Zeile ist ein Proxy
const line = cart.value.find((l) => l.id === id);
line.qty++;
// entfernen
const index = cart.value.findIndex((l) => l.id === id);
cart.value.splice(index, 1);
  • Vue beobachtet auch die Objekte im Array: line aus cart.value ist ein Proxy, und line.qty++ wird gesehen.
  • Der andere Weg funktioniert auch: ein neues Array in cart.value, zum Beispiel cart.value = cart.value.filter((l) => l.id !== id). Die Ref sieht die Änderung von .value.
  • Eine Änderung, die an der Ref vorbeigeht, wird nicht gesehen: eine einfache Variable mit einem neuen Array oder eine Kopie einer Zahl.

Probier es aus

Repariere „Entfernen“

Das ist der Warenkorb vom Anfang. Repariere remove so, dass die Zeile verschwindet und die Summe neu berechnet wird. Die Prüfung entfernt das Brot, dann den Käse.

Vue

Probier es aus

Finde den Fehler

„+“ erhöht die Menge nicht. Welche Zeile ist schuld?

Wähle die Zeile mit dem Fehler.

Die Idee

Eine Zeile des Arrays

Die Kundin drückt bei dem Brot auf „+“. Das qty eines Objekts muss sich ändern, und das Objekt steckt in einem Array.

In Vue ist line.qty++ der richtige Weg: Die Zeile aus cart.value ist ein Proxy, und Vue sieht die Änderung. Der Fehler ist, die Zahl aus dem Objekt herauszunehmen:

let { qty } = line; // eine Kopie der Zahl
qty++; // ändert die Kopie

Destrukturieren und let n = line.qty geben eine Kopie. Vue beobachtet das Objekt line, nicht jede Variable, in der die Zahl schon war. Schreib ins Objekt: line.qty++.

Genauer, für Neugierige

Tiefes Beobachten. ref und reactive machen auch die Objekte darin beobachtbar, sobald sie zum ersten Mal gelesen werden. shallowRef beobachtet nur .value: Mit ihr wird eine Änderung im Inneren nicht gesehen, und du setzt ein neues Array ein, wie in React.

reactive und Destrukturieren. const { total } = reactive({ total: 0 }) gibt eine Zahl, keinen beobachteten Wert. toRefs(obj) macht aus den Feldern Refs, die mit dem Objekt verbunden bleiben.

Das neue Array. cart.value = [...cart.value, line] funktioniert und macht ein neues Array, wie du es in React tun würdest. Das ist nützlich, wenn das alte Array gleich bleiben muss, zum Beispiel für „Rückgängig“.

structuredClone nimmt keinen Proxy: structuredClone(cart.value) wirft einen Fehler. Hol zuerst das einfache Array mit toRaw(cart.value).

Probier es aus

„+“ und „−“

Schreibe more und less. „+“ erhöht die Menge der Zeile um 1, und „−“ verringert sie um 1. Ist die Menge 1, entfernt „−“ die Zeile. Die Summe folgt der Menge.

Vue

Übung

Katalog und Warenkorb

Der Katalog hat vier Produkte mit einem Button „Hinzufügen“ (der Name für den Screenreader: „Hinzufügen Weißbrot“). Schreibe add und remove. Der erste Klick auf „Hinzufügen“ macht eine Zeile mit der Menge 1, und die nächsten erhöhen die Menge derselben Zeile, statt eine neue zu machen. „Entfernen“ entfernt die Zeile. Ist der Warenkorb leer, steht dort „Der Warenkorb ist leer“.

Vue

Test

Teste dich

1. let cart = reactive([...]); und dann cart = cart.filter(...). Warum ändert sich der Bildschirm nicht?
2. Welche Zeile erhöht die Menge einer Zeile richtig?
3. Funktioniert cart.value = [...cart.value, line] in Vue?
4. const cart = [ ... ] ohne ref, und dann cart.push(line). Was passiert?

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