Zum Inhalt springen

Reaktivität

Um Vue’s Reaktivitätssystem zu verstehen, müssen wir die JavaScript-Konzepte Getter/Setter und Proxy kennen.

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 aus
console.log(data.message); // Löst getter aus

Limitierungen von Object.defineProperty:

const data = { items: ['a', 'b'] };
// Neue Properties werden NICHT erkannt
data.newProp = 'value'; // Nicht reaktiv!
// Array-Index-Zuweisungen werden NICHT erkannt
data.items[2] = 'c'; // Nicht reaktiv!
// Array-Länge ändern wird NICHT erkannt
data.items.length = 0; // Nicht reaktiv!

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 (Object.defineProperty)Vue 3 (Proxy)
Neue Properties nicht reaktivNeue Properties automatisch reaktiv
Array-Index nicht reaktivArray-Operationen vollständig reaktiv
Vue.set() / Vue.delete() nötigKeine Workarounds nötig
Performance bei vielen PropertiesBessere Performance
Kein Support für Map/SetMap, Set, WeakMap, WeakSet

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() 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>

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>
<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>
<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 zugreifen
state.count++;
// ODER: toRefs() verwenden
const { count: countRef } = toRefs(state);
countRef.value++; // Reaktiv!
</script>
<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ücksetzen
function resetCorrect() {
state.count = 0;
}
// LÖSUNG 2: ref() verwenden wenn Ersetzung nötig
const stateRef = ref({ count: 0 });
stateRef.value = { count: 0 }; // Funktioniert!
</script>
<script setup>
import { ref } from 'vue';
const count = ref(0);
// FALSCH: .value vergessen
function increment() {
count++; // Ändert die Ref-Variable, nicht den Wert!
}
// RICHTIG
function incrementCorrect() {
count.value++;
}
// Tipp: Vue DevTools und TypeScript helfen hier!
</script>
<script setup>
import { reactive, ref } from 'vue';
// FALSCH: reactive() mit primitiven Werten
const count = reactive(0); // Funktioniert NICHT!
// RICHTIG: ref() für primitive Werte
const count = ref(0);
// FALSCH: String/Number/Boolean
const name = reactive('Max'); // Nein!
// RICHTIG
const name = ref('Max');
</script>
Aspektref()reactive()
Primitive WerteJaNein
Objekte/ArraysJaJa
Zugriff im Script.valueDirekt
Zugriff im TemplateUnwrappedDirekt
Objekt ersetzenMöglichBricht Reaktivität
DestrukturierungBricht ReaktivitätBricht Reaktivität
TypeScriptBessere InferenceKomplexer bei Generics
  • Primitive Werte (number, string, boolean)
  • Wert komplett ersetzt werden soll
  • Einzelne unabhängige Werte
  • Konsistenz bevorzugt wird

Vue bietet diverse Hilfsfunktionen für fortgeschrittene Reaktivitäts-Szenarien:

FunktionBeschreibung
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() 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 bleiben
count.value++; // Aktualisiert auch state.count
</script>

Für fortgeschrittene Anwendungsfälle bietet Vue weitere APIs:

  • shallowRef() / shallowReactive() - Nur oberste Ebene reaktiv
  • readonly() / shallowReadonly() - Schreibgeschützter Proxy
  • computed() - Berechnete reaktive Werte
  • watch() / watchEffect() - Reaktive Side Effects
  • effectScope() - Gruppierung von reaktiven Effekten

Offizielle Vue Dokumentation: