Option
HeimHeim Skill Webentwicklung vue-patterns

vue-patterns

affaan-m/ECC affaan-m/ECC

Bietet Muster für die Vue.js 3 Composition API, Komponentenarchitektur, Best Practices für Reaktivität, Pinia-Zustandsverwaltung, Vue Router-Navigation sowie Nuxt-SSR-Muster für Vue-, Nuxt-, Vite- oder Pinia-Projekte.

...Alle erweitern
0
Zeit aktualisiert 2. Oktober 2026

Vue.js-Muster und Best Practices

Umfassender Leitfaden für die Entwicklung mit Vue.js 3 unter Verwendung der Composition API (

Präsentations- vs. Container-Komponenten

  • Container-Komponenten: Eigene Datenabfrage, eigener Zustand und eigene Nebenwirkungen. Rendern von Präsentationskomponenten.
  • Präsentationskomponenten: Empfangen Props, senden Ereignisse. Keine API-Aufrufe, kein Zugriff auf den Store. Reines Rendering.

Best Practices für Props

// Typbasierte Props mit Standardwerten
interface Props {
  label: string;
  variant?: "primary" | "secondary";
  disabled?: boolean;
  items: Item[];
}

const props = withDefaults(defineProps(), {
  variant: "primary",
  disabled: false,
});
  • Geben Sie immer den Typ an und, falls erforderlich, auch die Erforderlichkeit undStandardwerte.
  • Boolesche Props: isXxx, hasXxx, canXxx.
  • Ändere Props niemals direkt – löse stattdessen Ereignisse aus.
  • Verwenden Sie für die v-model-Bindung defineModel() (Vue 3.4+) oder modelValue + update:modelValue.

Ereignisse

const emit = defineEmits<{
  submit: [];
  "update:modelValue": [value: string];
  select: [id: string, index: number];
}>();
  • Verwende Kebab-Case in Vorlagen (@update:model-value).
  • Verwenden Sie „camelCase“ im Skript (emit("update:modelValue", val)).

3. Composables (wiederverwendbare Logik)

Struktur

// composables/useDebounce.ts
export function useDebounce(value: MaybeRef, delay: number): Ref {
  const debounced = ref(toValue(value)) as Ref;

  let timer: ReturnType;
  watch(
    () => toValue(value),
    (newVal) => {
      clearTimeout(timer);
      timer = setTimeout(() => { debounced.value = newVal; }, delay);
    }
  );

  onUnmounted(() => clearTimeout(timer));
  return readonly(debounced);
}

Regeln

  • Muss mit dem Präfix „use“ beginnen.
  • Reaktive Werte (ref, computed, reactive) zurückgeben, niemals einfache Primitive.
  • Reaktive Eingaben über „MaybeRef“, „toRef() “ oder „toValue()“ akzeptieren.
  • Beseitigen Sie Nebenwirkungen in „onUnmounted “ oder im „watcher onCleanup“.
  • Keine Nebenwirkungen im Modulbereich.

vs. Mixins

Composables ersetzen Vue-2-Mixins vollständig:

  • Mixins: Undurchsichtiger Datenfluss, Konflikte bei der „Source of Truth“, Namenskonflikte.
  • Composables: Explizite Importe, eindeutige Rückgabewerte, komponierbar und „Tree-Shake“-fähig.

4. Zustandsverwaltung

Wann was verwenden

Muster Anwendungsfall
ref() / reactive() Lokaler Komponentenstatus
Props + Emits Kommunikation zwischen über- und untergeordneten Komponenten
Bereitstellen / Einbinden Theme-, Konfigurations- und Plugin-API
Pinia-Speicher Globaler, gemeinsam genutzter, komplexer Zustand
Server-Zustand, komponierbar API-Daten mit Caching ( Fetch/TanStack-Abfrage umschließen)

Pinia-Setup-Store (bevorzugt)

// stores/useCartStore.ts
export const useCartStore = defineStore("cart", () => {
  const items = ref([]);
  const isLoading = ref(false);

  const totalPrice = computed(() =>
    items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
  );
  const itemCount = computed(() =>
    items.value.reduce((sum, i) => sum + i.quantity, 0)
  );

  async function addItem(productId: string) {
    isLoading.value = true;
    try {
      const item = await fetchProduct(productId);
      const existing = items.value.find(i => i.id === item.id);
      if (existing) existing.quantity++;
      else items.value.push({ ...item, quantity: 1 });
    } finally {
      isLoading.value = false;
    }
  }

  return { items, isLoading, totalPrice, itemCount, addItem };
});
  • Verwende die „Setup Store“-Syntax (nicht „Options Store“).
  • Verwende vorzugsweise „actions“ für Mutationen auf Geschäftsebene und $patch() für gruppierte Aktualisierungen.
  • Jede asynchrone Aktion: Ladevorgang + Erfolg + Fehler behandeln.

5. Vue Router

Routendefinitionen

const routes = [
  {
    path: "/users/:id",
    name: "user-detail",
    component: () => import("@/pages/UserDetail.vue"), // lazy
    props: true, // Parameter als Props übergeben
    meta: { requiresAuth: true },
  },
];

Navigations-Guards

router.beforeEach((to, from) => {
  const { isLoggedIn } = useAuthStore();
  if (to.meta.requiresAuth && !isLoggedIn) {
    return { name: "login", query: { redirect: to.fullPath } };
  }
});

Reaktive Routenparameter

Wenn eine Komponente weiterhin gemountet bleibt, sich die Routenparameter jedoch ändern:

const route = useRoute();
const id = computed(() => route.params.id as string);
watch(id, (newId) => fetchItem(newId));

6. Vorlagenmuster

Vorlagensyntax


Wird geladen...
Fehler: {{ error }}
{{ content }}
Ein-/ausblendbarer Inhalt
{{ item.name }}
{{ item.name }}

7. Leistung

Technik Anwendungsfälle
v-memo Listenelemente, die sich selten ändern
v-once Inhalt, der einmal gerendert wird und für immer statisch bleibt
shallowRef() Große Datenstrukturen, die en bloc ersetzt werden
shallowReactive() Nur Eigenschaften der obersten Ebene sind reaktiv
v-show statt v-if Häufiges Umschalten der Sichtbarkeit
Zwischenspeichern von umgeschalteten Ansichten
Lazy-Routen () => import(...) für nicht kritische Routen
Suspense Asynchrones Laden von Komponenten mit Fallback

8. Testen

Stack

  • Vitest für Unit- und Komponententests
  • Vue Test Utils für das Einbinden und die Interaktion
  • @pinia/testing für das Mocking des Stores
  • Playwright für E2E-Tests

Komponententestmuster

import { mount } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
import UserCard from "./UserCard.vue";

beforeEach(() => { setActivePinia(createPinia()); });

it("rendet und sendet Signale", async () => {
  const wrapper = mount(UserCard, {
    props: { user: { id: "1", name: "Alice" } },
  });
  expect(wrapper.text()).toContain("Alice");
  await wrapper.find("button").trigger("click");
  expect(wrapper.emitted("select")![0]).toEqual(["1"]);
});

9. Nuxt-spezifische Muster

Automatische Importe

Nuxt importiert ref, computed, watch, useFetch, useAsyncData usw. automatisch. Verwenden Sie diese direkt, ohne sie zu importieren. Bei Nicht-Nuxt-Projekten sollten Sie sie immer explizit importieren.

useAsyncData / useFetch

const { data: user, pending, error, refresh } = await useAsyncData(
  "user", // eindeutiger Schlüssel für das Caching
  () => $fetch(`/api/users/${id}`),
);

const { data: posts } = await useFetch("/api/posts", {
  query: { page: 1 },
  key: "posts-page-1", // dupliziert Anfragen
});

Server-Routen

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const { id } = await getValidatedRouterParams(event, z.object({
    id: z.string().uuid(),
  }).parse);
  // ... abrufen und zurückgeben
});

Laufzeitkonfiguration

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // nur für den Server
    apiSecret: "",
    // öffentlich (für den Client sichtbar)
    public: {
      apiBase: "https://api.example.com",
    },
  },
});

10. Neue APIs in Vue 3.5+

Reaktive Props-Destrukturierung

Vue 3.5 hat die reaktive Destrukturierung von Props stabilisiert – aus ` defineProps()` destrukturierte Variablen sind automatisch reaktiv:

// Vue 3.5+: Destrukturierte Props sind reaktiv (kein „toRefs“ erforderlich)
const { count = 0, msg = "hello" } = defineProps<{
  count?: number;
  msg?: string;
}>();

// Einschränkung: Destrukturierte Props können nicht direkt überwacht werden
watch(() => count, (newVal) => { ... }); // PASS Getter erforderlich

useTemplateRef()

Ersetzen Sie einfache Refs mit übereinstimmenden Namen durch `useTemplateRef()` für Template-Referenzen:

import { useTemplateRef } from "vue";
const inputEl = useTemplateRef("input");
// „input“ entspricht dem Attribut ref="input" in der Vorlage, nicht dem Variablennamen

Unterstützt dynamische Ref-IDs: useTemplateRef(dynamicRefId).

onWatcherCleanup()

Global importierbare API zur Bereinigung von Watchern (Vue 3.5+). Sie muss synchron innerhalb des Watcher-Callbacks aufgerufen werden:

import { watch, onWatcherCleanup } from "vue";

watch(userId, async (newId) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  // ... mit Signal abrufen
});

useId()

SSR-stabile Generierung eindeutiger IDs für Formularelemente und Barrierefreiheit:

import { useId } from "vue";
const id = useId();

defer Teleport

ermöglicht das Teleportieren zu Zielen, die im selben Zyklus gerendert werden:

Inhalt

Lazy Hydration (SSR)

defineAsyncComponent() unterstützt nun die Hydrate -Strategie:

import { defineAsyncComponent, hydrateOnVisible } from "vue";
const AsyncComp = defineAsyncComponent({
  loader: () => import("./Comp.vue"),
  hydrate: hydrateOnVisible(),
});

Anti-Muster

Anti-Muster Warum das falsch ist Die Lösung
Destrukturierung von `defineProps() ` (Vue < 3.5) Erfasst einen Snapshot, verliert die Reaktivität Zugriff über props.xxx oder Verwendung von toRefs()
watch() auf destrukturierte Propanwenden (Vue 3.5+) Kompilierungsfehler – Destrukturierte Props können nicht direkt überwacht werden Getter-Wrapper verwenden: watch(() => count, ...)
v-if + v-for auf demselben Element Mehrdeutige Ausführungsreihenfolge Verwende ein berechnetes, gefiltertes Array
v-for -Schlüssel = Index Fehlerhafter Zustand bei Neuanordnung Verwende stabile Datenbank-IDs
Veränderliche Eigenschaften Verstößt gegen den unidirektionalen Datenfluss Ereignisse auslösen oder v-model verwenden
V-HTML mit Benutzerinhalten XSS-Sicherheitslücke Mit DOMPurify bereinigen
Mixins in Vue 3 Undurchsichtig, anfällig für Kollisionen Durch Composables ersetzen
Nebenwirkungen im Modulbereich von Composables Instanzenübergreifend Gültigkeitsbereich in onMounted + onUnmounted
reactive() für austauschbaren Zustand Ersetzen unterbricht die Reaktivität Verwenden Sie stattdessen ref()
Watcher ohne Bereinigung Speicherlecks, Race-Conditions Verwenden Sie onCleanup oder onWatcherCleanup() (Vue 3.5+)
Options-API im neuen Vue-3-Code Umstellung des Ökosystems auf die Composition-API Verwenden Sie

Alle Dateien

1 Dateien

vue-patterns installieren

Laden Sie die Skill-Dateien herunter und entpacken Sie sie in Ihr Verzeichnis „.claude/skills/“.

ZIP herunterladen

Klonen Sie das Repository und kopieren Sie die Skill-Dateien in Ihr Projekt.

git clone https://github.com/affaan-m/ECC/tree/main/skills/vue-patterns # Copy SKILL.md to your .claude/skills/ directory

Kopieren Kopieren
Schnelle Einrichtung: Kopiere den Skill-Ordner nach .claude/skills/ Claude erkennt den Skill automatisch und nutzt ihn.
Repository affaan-m/ECC

Ähnliche Skills

github-code-search
Zeit aktualisiert 29. Juni 2026
drizzle-orm
Zeit aktualisiert 29. Juni 2026
clickhouse-io
Zeit aktualisiert 29. Juni 2026
prisma-client-api
Zeit aktualisiert 29. Juni 2026