Skip to content

Kapitel 21: Composition API (Teil 1) - Einführung

📙 Lernziel: Warum Composition API? Unterschied zu Options API verstehen!


21.1 Was ist Composition API?

Composition API ist ein neuer Weg, um Vue-Komponenten zu erstellen (Vue 3+).

Zwei Wege in Vue 3:

  1. Options API (Vue 2 Stil) - data(), methods, computed, etc.
  2. Composition API (Vue 3 modern) - setup() oder <script setup>

Vergleich:

MerkmalOptions APIComposition API
LernkurveEinfacher (für Anfänger)Steiler (für Fortgeschrittene)
Logik-WiederverwendungMixins (schlecht)Composables (gut)
TypeScriptSchwierigEinfach
Logik-OrganisationNach OptionenNach Logik-Funktionen

21.2 Options API Beispiel

Options API (Vue 2 Stil):

vue
<script>
export default {
  // Daten
  data() {
    return {
      count: 0,
      message: 'Hallo'
    }
  },
  
  // Methoden
  methods: {
    increment() {
      this.count++
    },
    updateMessage() {
      this.message = 'Aktualisiert!'
    }
  },
  
  // Computed Properties
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
    
  // Watcher
  watch: {
    count(newValue, oldValue) {
      console.log(`Count: ${oldValue} → ${newValue}`)
    }
  },
    
  // Lebenszyklus
  mounted() {
    console.log('Komponente gemountet!')
  }
}
</script>

<template>
  <div>
    <p>Zähler: {{ count }}</p>
    <p>Doppelt: {{ doubleCount }}</p>
    <p>{{ message }}</p>
    <button @click="increment">Erhöhen</button>
    <button @click="updateMessage">Nachricht ändern</button>
  </div>
</template>

Probleme:

  • ❌ Logik ist nach Optionen organisiert, nicht nach Funktionen
  • ❌ Bei großen Komponenten schwer zu warten
  • ❌ Logik-Wiederverwendung mit Mixins ist fehleranfällig

21.3 Composition API Beispiel (<script setup>)

Composition API (<script setup>):

vue
<script setup>
import { ref, computed, watch, onMounted } from 'vue'

// Daten (reaktiv)
const count = ref(0)
const message = ref('Hallo')

// Methoden
const increment = () => {
  count.value++
}

const updateMessage = () => {
  message.value = 'Aktualisiert!'
}

// Computed Properties
const doubleCount = computed(() => count.value * 2)

// Watcher
watch(count, (newValue, oldValue) => {
  console.log(`Count: ${oldValue} → ${newValue}`)
})

// Lebenszyklus
onMounted(() => {
  console.log('Komponente gemountet!')
})
</script>

<template>
  <div>
    <p>Zähler: {{ count }}</p>
    <p>Doppelt: {{ doubleCount }}</p>
    <p>{{ message }}</p>
    <button @click="increment">Erhöhen</button>
    <button @click="updateMessage">Nachricht ändern</button>
  </div>
</template>

Vorteile:

  • ✅ Logik ist nach Funktionen organisiert
  • ✅ Bessere Lesbarkeit bei großen Komponenten
  • ✅ Logik-Wiederverwendung mit Composables
  • ✅ Bessere TypeScript-Unterstützung

21.4 setup() Funktion (Alternative)

setup() Funktion (ohne <script setup>):

vue
<script>
import { ref, computed, watch, onMounted } from 'vue'

export default {
  setup() {
    // Daten
    const count = ref(0)
    const message = ref('Hallo')
    
    // Methoden
    const increment = () => {
      count.value++
    }
    
    const updateMessage = () => {
      message.value = 'Aktualisiert!'
    }
    
    // Computed
    const doubleCount = computed(() => count.value * 2)
    
    // Watcher
    watch(count, (newValue, oldValue) => {
      console.log(`Count: ${oldValue} → ${newValue}`)
    })
    
    // Lebenszyklus
    onMounted(() => {
      console.log('Komponente gemountet!')
    })
    
    // Alles zurückgeben, was im Template genutzt werden soll
    return {
      count,
      message,
      increment,
      updateMessage,
      doubleCount
    }
  }
}
</script>

<template>
  <div>
    <p>Zähler: {{ count }}</p>
    <p>Doppelt: {{ doubleCount }}</p>
    <p>{{ message }}</p>
    <button @click="increment">Erhöhen</button>
  </div>
</template>

<script setup> vs setup():

  • <script setup> ist kürzer (kein return nötig)
  • <script setup> ist die empfohlene Schreibweise

21.5 Composables (Logik-Wiederverwendung)

Composables sind Funktionen, die Composition API nutzen, um Logik wiederzuverwenden.

Beispiel: useCounter.js (Composable)

javascript
// composables/useCounter.js
import { ref } from 'vue'

export function useCounter() {
  const count = ref(0)
  
  const increment = () => {
    count.value++
  }
  
  const decrement = () => {
    count.value--
  }
  
  const reset = () => {
    count.value = 0
  }
  
  return {
    count,
    increment,
    decrement,
    reset
  }
}

Verwendung in Komponente:

vue
<script setup>
import { useCounter } from '../composables/useCounter.js'

// Logik wiederverwenden
const { count, increment, decrement, reset } = useCounter()
</script>

<template>
  <div>
    <p>Zähler: {{ count }}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="reset">Zurücksetzen</button>
  </div>
</template>

Vorteile gegenüber Mixins:

  • ✅ Explizite Abhängigkeiten (keine Namenskonflikte)
  • ✅ Bessere TypeScript-Unterstützung
  • ✅ Einfacher zu testen

21.6 Übung: Options API zu Composition API migrieren

Aufgabe: Migriere diese Options API Komponente zu Composition API (<script setup>).

Options API (vorher):

vue
<script>
export default {
  data() {
    return {
      todos: [],
      newTodo: ''
    }
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim() !== '') {
        this.todos.push({
          id: Date.now(),
          text: this.newTodo,
          done: false
        })
        this.newTodo = ''
      }
    },
    removeTodo(id) {
      this.todos = this.todos.filter(todo => todo.id !== id)
    }
  },
  computed: {
    doneCount() {
      return this.todos.filter(todo => todo.done).length
    }
  }
}
</script>

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" />
    <button @click="addTodo">Hinzufügen</button>
    
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        <input type="checkbox" v-model="todo.done" />
        <span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">
          {{ todo.text }}
        </span>
        <button @click="removeTodo(todo.id)">Löschen</button>
      </li>
    </ul>
    
    <p>Erledigt: {{ doneCount }}</p>
  </div>
</template>

Composition API (nachher - Lösung):

vue
<script setup>
import { ref, computed } from 'vue'

const todos = ref([])
const newTodo = ref('')

const addTodo = () => {
  if (newTodo.value.trim() !== '') {
    todos.value.push({
      id: Date.now(),
      text: newTodo.value,
      done: false
    })
    newTodo.value = ''
  }
}

const removeTodo = (id) => {
  todos.value = todos.value.filter(todo => todo.id !== id)
}

const doneCount = computed(() => {
  return todos.value.filter(todo => todo.done).length
})
</script>

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" />
    <button @click="addTodo">Hinzufügen</button>
    
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        <input type="checkbox" v-model="todo.done" />
        <span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">
          {{ todo.text }}
        </span>
        <button @click="removeTodo(todo.id)">Löschen</button>
      </li>
    </ul>
    
    <p>Erledigt: {{ doneCount }}</p>
  </div>
</template>

✅ Zusammenfassung

In diesem Kapitel hast du gelernt:

  • ✅ Was Composition API ist
  • ✅ Unterschied zwischen Options API und Composition API
  • <script setup> Syntax
  • setup() Funktion (Alternative)
  • ✅ Composables (Logik-Wiederverwendung)
  • ✅ Options API zu Composition API migrieren

🎯 Nächster Schritt: In Kapitel 22 lernst du reaktive Grundlagen (ref() und reactive())!


← Zurück zu Kapitel 20: Lebenszyklus-HooksWeiter zu Kapitel 22: Reaktive Grundlagen →

Frei für alle Anfänger