Skip to content

Kapitel 8: Composables & kompositionelle Logik

In diesem Kapitel lernen Sie, was Composables sind, wie Sie diese erstellen und wie Sie logische Funktionen wiederverwenden.


8.1 Was sind Composables?

Composables sind Vue 3 Composition API-Funktionen, die wiederverwendbare Logik kapseln.

Unterschied: Vue Composables vs. Nuxt Composables

MerkmalVue ComposablesNuxt Composables
SpeicherortBeliebig (meist composables/)Automatisch in composables/
ImportManuellAuto-Import
SSR-UnterstützungManuellIntegriert
KontextVue-KontextNuxt-Kontext (useNuxtApp)

In Nuxt 3 werden alle Funktionen im composables/-Verzeichnis automatisch importiert!


8.2 Einfaches Composable erstellen

Beispiel: composables/useCounter.ts

typescript
// Einfaches Composable
export function useCounter() {
  const count = ref(0)

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

  const decrement = () => {
    count.value--
  }

  const reset = () => {
    count.value = 0
  }

  return {
    count: readonly(count),  // Schreibgeschützt exportieren
    increment,
    decrement,
    reset
  }
}

Verwendung (kein Import erforderlich!):

vue
<!-- pages/index.vue -->
<script setup>
// Kein Import! Nuxt importiert automatisch aus composables/
const { count, increment, decrement, reset } = useCounter()
</script>

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

8.3 Integrierte Nuxt Composables

Nuxt 3 bietet viele integrierte Composables:

Wichtigste Composables:

ComposableBeschreibung
useRoute()Aktuelle Route abrufen
useRouter()Router-Instanz
useFetch()Daten abrufen
useAsyncData()Asynchrone Daten
useState()Reaktiver Zustand
useCookie()Cookies verwalten
useHead()Meta-Tags setzen
useSeoMeta()SEO-Meta-Tags
useRuntimeConfig()Laufzeit-Konfiguration

Beispiel: useRoute() verwenden

vue
<script setup>
const route = useRoute()
const router = useRouter()

// Auf Route-Parameter zugreifen
const postId = route.params.id

// Programmatisch navigieren
const goToHome = () => {
  router.push('/')
}
</script>

8.4 Benutzerdefinierte Composables (Fortgeschritten)

Beispiel 1: useFetchPosts.ts (Datenabfrage kapseln)

typescript
// composables/useFetchPosts.ts
export function useFetchPosts() {
  const posts = ref([])
  const pending = ref(false)
  const error = ref(null)

  const fetchPosts = async () => {
    pending.value = true
    error.value = null

    try {
      const data = await $fetch('/api/posts')
      posts.value = data
    } catch (err) {
      error.value = err
    } finally {
      pending.value = false
    }
  }

  // Beim Erstellen sofort abrufen
  onMounted(() => {
    fetchPosts()
  })

  return {
    posts: readonly(posts),
    pending: readonly(pending),
    error: readonly(error),
    fetchPosts
  }
}

Verwendung:

vue
<script setup>
const { posts, pending, error, fetchPosts } = useFetchPosts()
</script>

<template>
  <div>
    <div v-if="pending">Lädt...</div>
    <div v-else-if="error">Fehler!</div>
    <ul v-else>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
    <button @click="fetchPosts">Aktualisieren</button>
  </div>
</template>

Beispiel 2: useWindowSize.ts (Browser-API kapseln)

typescript
// composables/useWindowSize.ts
export function useWindowSize() {
  const width = ref(0)
  const height = ref(0)

  const updateSize = () => {
    if (process.client) {
      width.value = window.innerWidth
      height.value = window.innerHeight
    }
  }

  onMounted(() => {
    updateSize()
    window.addEventListener('resize', updateSize)
  })

  onUnmounted(() => {
    if (process.client) {
      window.removeEventListener('resize', updateSize)
    }
  })

  return {
    width: readonly(width),
    height: readonly(height)
  }
}

Verwendung:

vue
<script setup>
const { width, height } = useWindowSize()
</script>

<template>
  <div>
    <p>Fensterbreite: {{ width }}px</p>
    <p>Fensterhöhe: {{ height }}px</p>
  </div>
</template>

8.5 Composables-Verzeichnisstruktur

Für größere Projekte sollten Sie Composables strukturieren:

composables/
├── useCounter.ts          # Einfache Composables
├── useFetchPosts.ts
├── useWindowSize.ts
├── auth/                 # Auth-bezogene Composables
│   ├── useAuth.ts
│   ├── useUser.ts
│   └── usePermissions.ts
├── posts/                # Post-bezogene Composables
│   ├── usePosts.ts
│   └── usePostDetail.ts
└── utils/               # Hilfs-Composables
    ├── useFormatDate.ts
    └── useDebounce.ts

8.6 Best Practices für Composables

1. Immer readonly() für zurückgegebene Refs verwenden

typescript
export function useCounter() {
  const count = ref(0)

  return {
    count: readonly(count),  // Verhindert externe Mutationen
    increment: () => count.value++
  }
}

2. Asynchrone Logik mit useAsyncData() kombinieren

typescript
export function usePosts() {
  const { data, pending, error, refresh } = useAsyncData(
    'posts',
    () => $fetch('/api/posts')
  )

  return {
    posts: data,
    pending,
    error,
    refresh
  }
}

3. Auf SSR-Kontext achten

typescript
export function useClientOnly() {
  const isClient = ref(false)

  onMounted(() => {
    isClient.value = true
  })

  return { isClient }
}

8.7 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • ✅ Was Composables sind (Vue 3 Composition API-Funktionen)
  • ✅ Den Unterschied zwischen Vue- und Nuxt-Composables
  • ✅ Eigene Composables im composables/-Verzeichnis zu erstellen
  • ✅ Nuxt's Auto-Import-Mechanismus zu nutzen
  • ✅ Fortgeschrittene Composables (Datenabfrage, Browser-APIs)
  • ✅ Best Practices für Composables

Nächste Schritte: Im nächsten Kapitel lernen wir SSR (Server-Side Rendering) & SSG (Static Site Generation) – die leistungsstarken Rendering-Modi von Nuxt.


📚 Weiterführende Ressourcen


Nächstes Kapitel: Kapitel 9: SSR & SSG →

Frei für alle Anfänger