Reaktivität
Um Vue’s Reaktivitätssystem zu verstehen, müssen wir die JavaScript-Konzepte Getter/Setter und Proxy kennen.
JavaScript Grundlagen
Abschnitt betitelt „JavaScript Grundlagen“Vue 2: Object.defineProperty
Abschnitt betitelt „Vue 2: Object.defineProperty“Mit Object.defineProperty() können wir Zugriffe auf Properties abfangen:
const data = { message: 'Hallo' };let internalValue = data.message;
Object.defineProperty(data, 'message', { get() { console.log('Gelesen:', internalValue); return internalValue; }, set(newValue) { console.log('Geändert:', internalValue, '->', newValue); internalValue = newValue; // Hier könnte Vue das DOM aktualisieren }});
data.message = 'Welt'; // Löst setter ausconsole.log(data.message); // Löst getter ausLimitierungen von Object.defineProperty:
const data = { items: ['a', 'b'] };
// Neue Properties werden NICHT erkanntdata.newProp = 'value'; // Nicht reaktiv!
// Array-Index-Zuweisungen werden NICHT erkanntdata.items[2] = 'c'; // Nicht reaktiv!
// Array-Länge ändern wird NICHT erkanntdata.items.length = 0; // Nicht reaktiv!Vue 3: Proxy
Abschnitt betitelt „Vue 3: Proxy“ES6 Proxy ermöglicht das Abfangen aller Operationen auf einem Objekt:
const data = { message: 'Hallo', items: ['a', 'b'] };
const proxy = new Proxy(data, { get(target, key) { console.log('Gelesen:', key); return target[key]; }, set(target, key, value) { console.log('Geändert:', key, '->', value); target[key] = value; return true; // Erfolg signalisieren }});
proxy.newProp = 'works!'; // Wird erkannt!proxy.items[2] = 'c'; // Wird erkannt!Vue 2 vs Vue 3
Abschnitt betitelt „Vue 2 vs Vue 3“| Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
|---|---|
| Neue Properties nicht reaktiv | Neue Properties automatisch reaktiv |
| Array-Index nicht reaktiv | Array-Operationen vollständig reaktiv |
Vue.set() / Vue.delete() nötig | Keine Workarounds nötig |
| Performance bei vielen Properties | Bessere Performance |
| Kein Support für Map/Set | Map, Set, WeakMap, WeakSet |
Composition API: ref()
Abschnitt betitelt „Composition API: ref()“ref() erstellt eine reaktive Referenz mit .value Property:
<script setup>import { ref } from 'vue';
const count = ref(0);const message = ref('Hallo');
function increment() { count.value++; // .value im Script notwendig! console.log(count.value);}</script>
<template> <!-- Automatisches Unwrapping im Template --> <button @click="increment">Count: {{ count }}</button> <p>{{ message }}</p></template>ref() mit Objekten
Abschnitt betitelt „ref() mit Objekten“ref() kann auch Objekte wrappen - diese werden intern zu reactive():
<script setup>import { ref } from 'vue';
const user = ref({ name: 'Max', age: 30});
function birthday() { user.value.age++; // .value für das Ref, dann Property}
function replaceUser() { // Komplettes Objekt ersetzen möglich! user.value = { name: 'Anna', age: 25 };}</script>Composition API: reactive()
Abschnitt betitelt „Composition API: reactive()“reactive() macht ein Objekt tief reaktiv:
<script setup>import { reactive } from 'vue';
const state = reactive({ count: 0, user: { name: 'Max', address: { city: 'Berlin' // Auch verschachtelte Objekte sind reaktiv } }});
function update() { state.count++; // Direkter Zugriff state.user.address.city = 'Hamburg'; // Deep Reactivity}</script>reactive() mit Arrays
Abschnitt betitelt „reactive() mit Arrays“<script setup>import { reactive } from 'vue';
const todos = reactive([ { id: 1, text: 'Lernen', done: false }, { id: 2, text: 'Üben', done: false }]);
function addTodo() { todos.push({ id: 3, text: 'Meistern', done: false });}
function removeTodo(index) { todos.splice(index, 1);}
function toggleTodo(todo) { todo.done = !todo.done; // Auch Objekte im Array sind reaktiv}</script>Häufige Fehler (Pitfalls)
Abschnitt betitelt „Häufige Fehler (Pitfalls)“Pitfall #1: Destrukturierung
Abschnitt betitelt „Pitfall #1: Destrukturierung“<script setup>import { reactive, toRefs } from 'vue';
const state = reactive({ count: 0, name: 'Max' });
// FALSCH: Destrukturierung bricht Reaktivität!let { count } = state;count++; // Aktualisiert NICHT das DOM!
// RICHTIG: Direkt auf state zugreifenstate.count++;
// ODER: toRefs() verwendenconst { count: countRef } = toRefs(state);countRef.value++; // Reaktiv!</script>Pitfall #2: reactive() Objekt ersetzen
Abschnitt betitelt „Pitfall #2: reactive() Objekt ersetzen“<script setup>import { reactive, ref } from 'vue';
let state = reactive({ count: 0 });
function reset() { // FALSCH: Ersetzt die Referenz, nicht das Objekt! state = reactive({ count: 0 }); // Template hat noch alte Referenz}
// LÖSUNG 1: Properties einzeln zurücksetzenfunction resetCorrect() { state.count = 0;}
// LÖSUNG 2: ref() verwenden wenn Ersetzung nötigconst stateRef = ref({ count: 0 });stateRef.value = { count: 0 }; // Funktioniert!</script>Pitfall #3: .value vergessen
Abschnitt betitelt „Pitfall #3: .value vergessen“<script setup>import { ref } from 'vue';
const count = ref(0);
// FALSCH: .value vergessenfunction increment() { count++; // Ändert die Ref-Variable, nicht den Wert!}
// RICHTIGfunction incrementCorrect() { count.value++;}
// Tipp: Vue DevTools und TypeScript helfen hier!</script>Pitfall #4: Primitive mit reactive()
Abschnitt betitelt „Pitfall #4: Primitive mit reactive()“<script setup>import { reactive, ref } from 'vue';
// FALSCH: reactive() mit primitiven Wertenconst count = reactive(0); // Funktioniert NICHT!
// RICHTIG: ref() für primitive Werteconst count = ref(0);
// FALSCH: String/Number/Booleanconst name = reactive('Max'); // Nein!
// RICHTIGconst name = ref('Max');</script>ref() vs reactive()
Abschnitt betitelt „ref() vs reactive()“| Aspekt | ref() | reactive() |
|---|---|---|
| Primitive Werte | Ja | Nein |
| Objekte/Arrays | Ja | Ja |
| Zugriff im Script | .value | Direkt |
| Zugriff im Template | Unwrapped | Direkt |
| Objekt ersetzen | Möglich | Bricht Reaktivität |
| Destrukturierung | Bricht Reaktivität | Bricht Reaktivität |
| TypeScript | Bessere Inference | Komplexer bei Generics |
Empfehlung
Abschnitt betitelt „Empfehlung“- Primitive Werte (number, string, boolean)
- Wert komplett ersetzt werden soll
- Einzelne unabhängige Werte
- Konsistenz bevorzugt wird
- Zusammengehörige Daten (State-Objekt)
- Keine Ersetzung des Objekts nötig
- Kein
.valuegewünscht - Ähnlich zu Options API
data()
Reaktivitäts-Helfer
Abschnitt betitelt „Reaktivitäts-Helfer“Vue bietet diverse Hilfsfunktionen für fortgeschrittene Reaktivitäts-Szenarien:
| Funktion | Beschreibung |
|---|---|
toRef() | Einzelne Property zu Ref konvertieren |
toRefs() | Alle Properties zu Refs konvertieren |
toValue() | Ref oder Getter zu Wert normalisieren |
isRef() | Prüft ob Wert ein Ref ist |
unref() | Gibt .value zurück oder den Wert selbst |
isReactive() | Prüft ob Objekt reaktiv ist |
isProxy() | Prüft ob Wert ein Proxy ist |
toRefs()
Abschnitt betitelt „toRefs()“toRefs() ermöglicht Destrukturierung ohne Reaktivitätsverlust:
<script setup>import { reactive, toRefs } from 'vue';
const state = reactive({ count: 0, name: 'Max'});
// Destrukturierung mit toRefs()const { count, name } = toRefs(state);
// Jetzt sind count und name Refs, die mit state synchron bleibencount.value++; // Aktualisiert auch state.count</script>Weitere Reaktivitäts-APIs
Abschnitt betitelt „Weitere Reaktivitäts-APIs“Für fortgeschrittene Anwendungsfälle bietet Vue weitere APIs:
shallowRef()/shallowReactive()- Nur oberste Ebene reaktivreadonly()/shallowReadonly()- Schreibgeschützter Proxycomputed()- Berechnete reaktive Wertewatch()/watchEffect()- Reaktive Side EffectseffectScope()- Gruppierung von reaktiven Effekten
Dokumentation
Abschnitt betitelt „Dokumentation“Offizielle Vue Dokumentation: