option

vue-patterns

affaan-m/ECC affaan-m/ECC

Présente les modèles de l'API de composition de Vue.js 3, l'architecture des composants, les meilleures pratiques en matière de réactivité, la gestion d'état avec Pinia, la navigation avec Vue Router, ainsi que les modèles de SSR Nuxt pour les projets Vue, Nuxt, Vite ou Pinia.

...Développer tout
0
Heure mise à jour 2 octobre 2026

Modèles et bonnes pratiques Vue.js

Guide complet pour le développement avec Vue.js 3 à l'aide de l'API de composition (

Composants de présentation vs composants conteneurs

  • Composants conteneurs: gèrent eux-mêmes la récupération des données, l’état et les effets secondaires. Affichent les composants de présentation.
  • Composants de présentation: reçoivent des props, émettent des événements. Pas d’appels API, pas d’accès au store. Rendu pur.

Bonnes pratiques concernant les props

// Props basées sur les types avec valeurs par défaut
interface Props {
  label: string;
  variant?: "primary" | "secondary";
  disabled?: boolean;
  items: Item[];
}

const props = withDefaults(defineProps(), {
  variant: "primary",
  disabled: false,
});
  • Indiquez toujours le type, ainsi que les valeursobligatoires et par défaut le cas échéant.
  • Props booléennes : isXxx, hasXxx, canXxx.
  • Ne modifiez jamais les props — émettez plutôt des événements.
  • Pour la liaison v-model, utilisez defineModel() (Vue 3.4+) ou modelValue + update:modelValue.

Événements

const emit = defineEmits<{
  submit: [];
  "update:modelValue": [value: string];
  select: [id: string, index: number];
}>();
  • Utilisez la notation « kebab-case » dans les modèles (@update:model-value).
  • Utilisez le format « camelCase » dans le script (emit("update:modelValue", val)).

3. Composables (logique réutilisable)

Structure

// 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);
}

Règles

  • Doit commencer par le préfixe « use ».
  • Renvoie des valeurs réactives (ref, computed, reactive), jamais de types primitifs simples.
  • Accepter les entrées réactives via MaybeRef / toRef() / toValue().
  • Nettoyer les effets secondaires dans `onUnmounted ` ou dans ` onCleanup` du watcher.
  • Pas d’effets secondaires au niveau du module.

vs Mixins

Les composables remplacent entièrement les mixins de Vue 2 :

  • Mixins: flux de données opaque, conflits de « source de vérité », conflits de noms.
  • Composables: importations explicites, valeurs de retour claires, composables et optimisables par « tree-shaking ».

4. Gestion de l'état

Quand utiliser quoi

Modèle Cas d’utilisation
ref() / reactive() État d'un composant local
Props + Émissions Communication parent-enfant
Fournir / Injecter API de thème, de configuration et de plugin
Magasin Pinia État global, partagé et complexe
État du serveur modulable Données API avec mise en cache (wrap fetch/TanStack Query)

Configuration de Pinia Store (recommandé)

// 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 };
});
  • Utilisez la syntaxe Setup Store (et non Options Store).
  • Privilégiez les actions pour les mutations au niveau métier et $patch() pour les mises à jour groupées.
  • Pour chaque action asynchrone : gérez le chargement, la réussite et l’erreur.

5. Vue Router

Définitions de routes

const routes = [
  {
    path: "/users/:id",
    name: "user-detail",
    component: () => import("@/pages/UserDetail.vue"), // chargement différé
    props: true, // transmettre les paramètres en tant que props
    meta: { requiresAuth: true },
  },
];

Contrôles d'accès

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

Paramètres de route réactifs

Lorsqu’un composant reste monté mais que les paramètres de route changent :

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

6. Modèles de gabarits

Syntaxe des modèles


Chargement…
Erreur : {{ error }}
{{ content }}
Contenu basculé
{{ item.name }}
{{ item.name }}

7. Performances

Technique Quand l'utiliser
v-memo Éléments de liste qui changent rarement
v-once Contenu affiché une seule fois et statique pour toujours
shallowRef() Structures de données volumineuses remplacées dans leur intégralité
shallowReactive() Seules les propriétés de niveau supérieur sont réactives
v-show plutôt que v-if Changements fréquents de visibilité
Mise en cache des vues dont la visibilité a été modifiée
Routes différées () => import(...) pour les routes non critiques
Suspense Chargement asynchrone des composants avec solution de secours

8. Tests

Stack

  • Vitest pour les tests unitaires et de composants
  • Vue Test Utils pour le montage et les interactions
  • @pinia/testing pour la simulation du store
  • Playwright pour les tests E2E

Modèle de test de composant

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

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

it("s'affiche et émet", 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. Modèles spécifiques à Nuxt

Importations automatiques

Nuxt importe automatiquement ref, computed, watch, useFetch, useAsyncData, etc. Utilisez-les directement sans les importer. Pour les projets non Nuxt, importez-les toujours explicitement.

useAsyncData / useFetch

const { data: user, pending, error, refresh } = await useAsyncData(
  "user", // clé unique pour la mise en cache
  () => $fetch(`/api/users/${id}`),
);

const { data: posts } = await useFetch("/api/posts", {
  query: { page: 1 },
  key: "posts-page-1", // déduplique les requêtes
});

Routes du serveur

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const { id } = await getValidatedRouterParams(event, z.object({
    id: z.string().uuid(),
  }).parse);
  // ... récupération et retour
});

Configuration d'exécution

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // serveur uniquement
    apiSecret: "",
    // public (exposé au client)
    public: {
      apiBase: "https://api.example.com",
    },
  },
});

10. Nouvelles API de Vue 3.5+

Déstructuration des props réactives

Vue 3.5 a stabilisé la déstructuration des props réactifs — les variables déstructurées issues de defineProps() sont automatiquement réactives :

// Vue 3.5+ : les props déstructurées sont réactives (pas besoin de toRefs)
const { count = 0, msg = "hello" } = defineProps<{
  count?: number;
  msg?: string;
}>();

// Limitation : impossible de surveiller directement une prop déstructurée
watch(() => count, (newVal) => { ... }); // REQUIS : un getter est nécessaire

useTemplateRef()

Remplacez les refs simples portant le même nom par useTemplateRef() pour les références de modèle :

import { useTemplateRef } from "vue";
const inputEl = useTemplateRef("input");
// « input » correspond à l’attribut ref="input" du modèle, et non au nom de la variable

Prend en charge les identifiants de ref dynamiques : useTemplateRef(dynamicRefId).

onWatcherCleanup()

API de nettoyage des observateurs pouvant être importée globalement (Vue 3.5+). Elle doit être appelée de manière synchrone à l'intérieur de la fonction de rappel de l'observateur :

import { watch, onWatcherCleanup } from "vue";

watch(userId, async (newId) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  // ... récupération avec le signal
});

useId()

Génération d’un identifiant unique compatible avec le SSR pour les éléments de formulaire et l’accessibilité :

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

defer Teleport

permet de se téléporter vers des cibles rendues au cours du même cycle :

Contenu

Hydratation paresseuse (SSR)

defineAsyncComponent() prend désormais en charge la stratégie d'hydratation:

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

Anti-modèles

Anti-modèle Pourquoi c'est une erreur La solution
Déstructuration de defineProps() (Vue < 3.5) Capture un instantané, perd la réactivité Accéder via props.xxx ou utiliser toRefs()
watch() sur la propriété déstructurée (Vue 3.5+) Erreur de compilation — les props déstructurées ne peuvent pas être surveillées directement Utiliser un wrapper getter : watch(() => count, ...)
v-if + v-for sur le même élément Ordre d'exécution ambigu Utilisez un tableau filtré calculé
v-for key = index État corrompu en cas de réorganisation Utiliser des identifiants de base de données stables
Props modifiables Violation du flux de données unidirectionnel Émettre des événements ou utiliser v-model
v-html avec du contenu utilisateur Vulnérabilité XSS Nettoyer avec DOMPurify
Mixins dans Vue 3 Opaques, sujets aux collisions Remplacer par des composables
Effets secondaires au niveau du module dans les composables Partagés entre les instances Portée dans onMounted + onUnmounted
reactive() pour un état remplaçable Le remplacement rompt la réactivité Utilisez plutôt ref()
Watcher sans nettoyage Fuites de mémoire, conditions de concurrence Utilisez onCleanup ou onWatcherCleanup() (Vue 3.5+)
API Options dans le nouveau code Vue 3 Passage de l'écosystème à l'API de composition Utilisation

Tous les fichiers

1 fichiers

Installer vue-patterns

Téléchargez et décompressez les fichiers de compétences dans votre répertoire .claude/skills/.

Télécharger le ZIP

Clonez le dépôt et copiez les fichiers de compétence dans votre projet.

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

Copier Copier
Configuration rapide: Copiez le dossier de la compétence dans .claude/skills/ Claude détectera automatiquement la compétence et l'utilisera
Dépôt affaan-m/ECC

Compétences similaires

github-code-search
Heure mise à jour 29 juin 2026
drizzle-orm
Heure mise à jour 29 juin 2026
clickhouse-io
Heure mise à jour 29 juin 2026
prisma-client-api
Heure mise à jour 29 juin 2026