Skip to content

Kapitel 18: Komponenten-Kommunikation (Teil 3) - Provide & Inject

📙 Lernziel: Daten von Vorfahren zu Nachkommen mit provide() und inject() weitergeben!


18.1 Was sind Provide & Inject?

Problem: Props mehrfach weitergeben ("Prop Drilling").

Lösung: provide() und inject() - Daten direkt an Nachkommen weitergeben.

Datenfluss: Vorfahre → Alle Nachkommen (überspringt Zwischenkomponenten)

Beispiel-Szenario:

App.vue (Vorfahre)
  └─ Layout.vue (Zwischenkomponente - braucht Props nicht)
       └─ Sidebar.vue (Nachkomme)
            Erhält Daten direkt von App.vue

18.2 provide() in Vorfahre

provide() stellt Daten für alle Nachkommen bereit.

Syntax:

javascript
import { provide } from 'vue'

provide(schlüssel, wert)

Beispiel:

vue
<!-- App.vue (Vorfahre) -->
<script setup>
import { provide, ref } from 'vue'
import Layout from './components/Layout.vue'

// Daten bereitstellen
const theme = ref('light')
const user = ref({ name: 'Max', role: 'admin' })

provide('theme', theme)
provide('user', user)
</script>

<template>
  <div :class="`theme-${theme}`">
    <h1>Meine App</h1>
    <Layout />
  </div>
</template>

18.3 inject() in Nachkomme

inject() ruft bereitgestellte Daten ab.

Syntax:

javascript
import { inject } from 'vue'

const wert = inject(schlüssel, fallbackWert)

Beispiel:

vue
<!-- Sidebar.vue (Nachkomme) -->
<script setup>
import { inject, ref } from 'vue'

// Daten abrufen
const theme = inject('theme')
const user = inject('user')
const nonExistent = inject('nicht-existent', 'Standardwert')
</script>

<template>
  <div class="sidebar">
    <p>Aktuelles Theme: {{ theme }}</p>
    <p>Benutzer: {{ user.name }} ({{ user.role }})</p>
  </div>
</template>

Wichtig:

  • ✅ Nachkommen können bereitgestellte Daten ändern (Vorsicht!)
  • ✅ Änderungen sind reaktiv

18.4 Reaktive Provide/Inject (Best Practice)

Problem: Nachkommen sollten Provide-Daten nicht direkt ändern.

Lösung: Funktionen bereitstellen, um Daten zu ändern.

Beispiel:

vue
<!-- App.vue (Vorfahre) -->
<script setup>
import { provide, ref } from 'vue'

const theme = ref('light')

// Funktion bereitstellen
const toggleTheme = () => {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}

provide('theme', theme)
provide('toggleTheme', toggleTheme)
</script>

<template>
  <div :class="`theme-${theme}`">
    <h1>Meine App</h1>
    <button @click="toggleTheme">Theme wechseln</button>
    <Layout />
  </div>
</template>

Verwendung in Nachkomme:

vue
<!-- Sidebar.vue (Nachkomme) -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>

<template>
  <div class="sidebar">
    <p>Theme: {{ theme }}</p>
    <button @click="toggleTheme">Wechseln</button>
  </div>
</template>

18.5 Symbol als Schlüssel (Beste Praxis)

Problem: String-Schlüssel können kollidieren.

Lösung: Symbol als Schlüssel verwenden.

Beispiel:

javascript
// keys.js (separate Datei)
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')
vue
<!-- App.vue -->
<script setup>
import { provide, ref } from 'vue'
import { THEME_KEY, USER_KEY } from './keys.js'

const theme = ref('light')
const user = ref({ name: 'Max' })

provide(THEME_KEY, theme)
provide(USER_KEY, user)
</script>
vue
<!-- Sidebar.vue -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY, USER_KEY } from '../keys.js'

const theme = inject(THEME_KEY)
const user = inject(USER_KEY)
</script>

18.6 Übung: Einfacher State Management

Aufgabe: Erstelle einen einfachen State Management mit provide/inject.

Lösung:

vue
<!-- App.vue -->
<script setup>
import { provide, ref, computed } from 'vue'
import Layout from './components/Layout.vue'

// State
const todos = ref([
  { id: 1, text: 'Lernen', done: false },
  { id: 2, text: 'Sport', done: true }
])

// Actions
const addTodo = (text) => {
  todos.value.push({
    id: Date.now(),
    text,
    done: false
  })
}

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

const toggleTodo = (id) => {
  const todo = todos.value.find(t => t.id === id)
  if (todo) todo.done = !todo.done
}

// Computed
const doneCount = computed(() => todos.value.filter(t => t.done).length)

// Bereitstellen
provide('todos', todos)
provide('addTodo', addTodo)
provide('removeTodo', removeTodo)
provide('toggleTodo', toggleTodo)
provide('doneCount', doneCount)
</script>

<template>
  <div>
    <h1>To-Do App</h1>
    <Layout />
  </div>
</template>

Verwendung in Nachkomme:

vue
<!-- TodoList.vue -->
<script setup>
import { inject, computed } from 'vue'

const todos = inject('todos')
const addTodo = inject('addTodo')
const removeTodo = inject('removeTodo')
const toggleTodo = inject('toggleTodo')
const doneCount = inject('doneCount')

const newTodo = ref('')
</script>

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo(newTodo); newTodo = ''" />
    <button @click="addTodo(newTodo); newTodo = ''">Hinzufügen</button>
    
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        <input type="checkbox" :checked="todo.done" @change="toggleTodo(todo.id)" />
        <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 provide/inject sind (Lösung für Prop Drilling)
  • provide() in Vorfahre nutzen
  • inject() in Nachkomme nutzen
  • ✅ Reaktive Provide/Inject (Best Practice)
  • ✅ Symbol als Schlüssel (Beste Praxis)
  • ✅ Praxis: Einfacher State Management

🎯 Nächster Schritt: In Kapitel 19 lernst du Komponenten-Slots!


← Zurück zu Kapitel 17: EmitsWeiter zu Kapitel 19: Slots →

Frei für alle Anfänger