Provide / Inject
Provide/Inject ermöglicht es, Daten von einer Eltern-Komponente an beliebig tief verschachtelte Kind-Komponenten weiterzugeben - ohne Props durch jede Ebene zu reichen.
Das Problem: Prop Drilling
Abschnitt betitelt „Das Problem: Prop Drilling“┌─────────────────────────────────────────┐│ App (hat theme) ││ └─ Layout (braucht theme nicht) ││ └─ Sidebar (braucht theme nicht) ││ └─ Menu (braucht theme nicht)││ └─ MenuItem (braucht theme!) │└─────────────────────────────────────────┘<!-- Jede Ebene muss prop weiterreichen --><Layout :theme="theme"> <Sidebar :theme="theme"> <Menu :theme="theme"> <MenuItem :theme="theme" /><!-- App providet -->provide('theme', theme)
<!-- MenuItem injiziert direkt -->const theme = inject('theme')Provide: Composition API
Abschnitt betitelt „Provide: Composition API“<script setup>import { provide, ref, readonly } from 'vue'
const theme = ref('dark')const user = ref({ name: 'Max', role: 'admin' })
// Einfacher Wertprovide('theme', theme)
// Readonly um Mutation von Kindern zu verhindernprovide('user', readonly(user))
// Objekt mit Werten und Methodenprovide('auth', { user, login: (credentials) => { /* ... */ }, logout: () => { user.value = null }})</script>Inject: Composition API
Abschnitt betitelt „Inject: Composition API“<script setup>import { inject } from 'vue'
// Einfaches Injectconst theme = inject('theme')
// Mit Default-Wertconst locale = inject('locale', 'de')
// Mit Factory-Funktion als Defaultconst config = inject('config', () => ({ debug: false }), true)
// Readonly Userconst user = inject('user')console.log(user.value.name) // 'Max'// user.value.name = 'Anna' // Warnung wenn readonly</script>
<template> <div :class="theme"> Willkommen, {{ user?.name }} </div></template>Options API
Abschnitt betitelt „Options API“<script>export default { data() { return { theme: 'dark' } }, // Als Funktion für reaktive Daten provide() { return { theme: this.theme, // So nicht reaktiv! } }}</script><script>export default { inject: ['theme'], // Oder mit Optionen inject: { theme: { from: 'theme', default: 'light' } }, mounted() { console.log(this.theme) }}</script>Reaktivität sicherstellen
Abschnitt betitelt „Reaktivität sicherstellen“<!-- Provider --><script setup>import { provide, ref, computed } from 'vue'
const count = ref(0)
// Ref direkt providieren - bleibt reaktivprovide('count', count)
// Computed providierenprovide('doubleCount', computed(() => count.value * 2))
// Reaktives Objektprovide('state', reactive({ count: 0, name: 'Max' }))</script><!-- Consumer --><script setup>import { inject } from 'vue'
const count = inject('count')// count ist ein Ref - reaktiv!
const doubleCount = inject('doubleCount')// Aktualisiert sich automatisch</script>Mutation: Best Practice
Abschnitt betitelt „Mutation: Best Practice“Änderungen sollten nur dort passieren, wo die Daten providet werden:
<!-- Provider --><script setup>import { provide, ref, readonly } from 'vue'
const count = ref(0)
// Methode zum Ändernfunction increment() { count.value++}
// Readonly Wert + Methode providierenprovide('counter', { count: readonly(count), increment})</script><!-- Consumer --><script setup>const { count, increment } = inject('counter')
// count.value++ // Nicht möglich (readonly)increment() // Über bereitgestellte Methode</script>Symbol Keys für Type Safety
Abschnitt betitelt „Symbol Keys für Type Safety“import type { InjectionKey, Ref } from 'vue'
export interface User { id: number name: string role: 'admin' | 'user'}
// Typisierter Injection Keyexport const userKey: InjectionKey<Ref<User | null>> = Symbol('user')export const themeKey: InjectionKey<Ref<'light' | 'dark'>> = Symbol('theme')<script setup lang="ts">import { provide, inject, ref } from 'vue'import { userKey, themeKey } from './keys'
// Provider - TypeScript kennt den Typprovide(userKey, ref({ id: 1, name: 'Max', role: 'admin' }))
// Consumer - Automatisch typisiertconst user = inject(userKey) // Ref<User | null> | undefinedconst theme = inject(themeKey, ref('light')) // Ref<'light' | 'dark'></script>App-Level Provide
Abschnitt betitelt „App-Level Provide“import { createApp, ref } from 'vue'import App from './App.vue'
const app = createApp(App)
// Global für alle Komponenten verfügbarapp.provide('appName', 'Meine App')app.provide('apiUrl', 'https://api.example.com')
// Auch reaktive Werteconst globalTheme = ref('light')app.provide('theme', globalTheme)
app.mount('#app')<!-- Beliebige Komponente --><script setup>const appName = inject('appName') // 'Meine App'const theme = inject('theme') // Ref<'light'></script>Anwendungsfall: Theme Provider
Abschnitt betitelt „Anwendungsfall: Theme Provider“<script setup>import { provide, ref, computed } from 'vue'
const isDark = ref(false)
const theme = computed(() => ({ mode: isDark.value ? 'dark' : 'light', colors: isDark.value ? { bg: '#1a1a1a', text: '#ffffff' } : { bg: '#ffffff', text: '#1a1a1a' }}))
function toggleTheme() { isDark.value = !isDark.value}
provide('theme', { theme, toggleTheme })</script>
<template> <div :class="theme.mode"> <slot /> </div></template>Props vs Provide/Inject
Abschnitt betitelt „Props vs Provide/Inject“- Direkte Eltern-Kind-Beziehung
- Explizite Datenflüsse gewünscht
- Komponente wiederverwendbar sein soll
- 1-2 Ebenen Tiefe
- Tiefe Verschachtelung (3+ Ebenen)
- Viele Komponenten brauchen gleiche Daten
- Plugin/Library-Entwicklung
- Globale Konfiguration
- Context-Pattern (Theme, Auth, i18n)
Best Practices
Abschnitt betitelt „Best Practices“- Symbol Keys für Type Safety
readonly()für schreibgeschützte Daten- Methoden für Mutationen providieren
- Defaults bei
inject()angeben - Für echte DI-Szenarien nutzen
- Als Ersatz für Pinia/State Management
- String Keys in großen Apps
- Reaktive Daten ohne readonly
- Tiefe Objektmutationen erlauben
- Zu viele unabhängige provides
Dokumentation
Abschnitt betitelt „Dokumentation“Offizielle Vue Dokumentation:
API Referenz: