Skip to content
Back to skills

Dev Vue Guide

ASecurity

Développement d'applications Vue.js 3 avec Composition API, Pinia, Vue Router, composants réactifs et bonnes pratiques. Se déclenche avec "Vue", "Vue.js", "Composition API", "Pinia", "Vue Router", "composant Vue". Also triggers on "Vue 3 app", "Pinia store".

  • 27 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
ai-agentstypescriptbashreactvueapiperformance

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 18, 2026

npx -y skills add khalilbenaz/claude-skills-collection --skill dev-vue-guide --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dev Vue Guide?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dev Vue Guide
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/khalilbenaz-dev-vue-guide/badge)](https://www.skillsdirectory.com/skills/khalilbenaz-dev-vue-guide)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dev-vue-guide
description: Développement d'applications Vue.js 3 avec Composition API, Pinia, Vue Router, composants réactifs et bonnes pratiques. Se déclenche avec "Vue", "Vue.js", "Composition API", "Pinia", "Vue Router", "composant Vue". Also triggers on "Vue 3 app", "Pinia store".
---

# Guide Vue.js 3

## 1. Scaffolding du projet

```bash
# Nouveau projet Vite + Vue 3 + TS
npm create vite@latest mon-app -- --template vue-ts
cd mon-app && npm install

# Ajout immédiat des dépendances core
npm install pinia vue-router@4
npm install -D vitest @vue/test-utils jsdom
```

**Arborescence cible :**
```
src/
  components/   # composants réutilisables (atomiques)
  views/        # pages associées à une route
  composables/  # logique partagée (use*)
  stores/       # stores Pinia
  router/       # index.ts + guards
  types/        # interfaces TS globales
```

---

## 2. Composants — Script Setup + TypeScript

Toujours `<script setup lang="ts">`. Ne jamais utiliser l'Options API dans du code nouveau.

```vue
<script setup lang="ts">
import { ref, computed } from 'vue'

interface Props {
  title: string
  count?: number
}

const props = withDefaults(defineProps<Props>(), { count: 0 })
const emit = defineEmits<{ increment: [value: number] }>()

const doubled = computed(() => props.count * 2)

function handleClick() {
  emit('increment', props.count + 1)
}
</script>

<template>
  <button @click="handleClick">{{ title }} — {{ doubled }}</button>
</template>
```

**Critères `ref` vs `reactive` :**
| Cas | Outil |
|-----|-------|
| Valeur primitive ou objet unique interchangeable | `ref()` |
| Objet complexe multi-propriétés | `reactive()` |
| Gros objet immuable côté UI | `shallowRef()` |
| Prop réactive transmise à un composable | `toRef(props, 'key')` |

---

## 3. Pinia — Gestion d'état

```ts
// src/stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  const name = ref('')
  const isAdmin = computed(() => name.value.startsWith('admin_'))

  async function fetchUser(id: string) {
    const data = await api.get(`/users/${id}`)
    name.value = data.name
  }

  return { name, isAdmin, fetchUser }
})
```

```vue
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'

const store = useUserStore()
const { name, isAdmin } = storeToRefs(store)  // réactivité conservée
// store.fetchUser — actions appelées directement, sans storeToRefs
</script>
```

**Persistance (pinia-plugin-persistedstate) :**
```ts
// main.ts
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
pinia.use(piniaPluginPersistedstate)

// dans le store : ajouter { persist: true } comme 3e argument de defineStore
```

---

## 4. Vue Router — Configuration et Guards

```ts
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/dashboard',
      component: () => import('@/views/Dashboard.vue'), // lazy load obligatoire
      meta: { requiresAuth: true },
    },
    {
      path: '/user/:id',
      component: () => import('@/views/UserProfile.vue'),
      props: true,  // injecte :id comme prop
    },
  ],
})

router.beforeEach((to) => {
  const auth = useAuthStore()
  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return { name: 'Login', query: { redirect: to.fullPath } }
  }
})
```

---

## 5. Composables — Extraction de logique

Convention : fichier `src/composables/useXxx.ts`, retour d'un objet de refs.

```ts
// src/composables/useFetch.ts
import { ref } from 'vue'

export function useFetch<T>(url: string) {
  const data = ref<T | null>(null)
  const error = ref<Error | null>(null)
  const loading = ref(false)

  async function execute() {
    loading.value = true
    try {
      const res = await fetch(url)
      data.value = await res.json()
    } catch (e) {
      error.value = e as Error
    } finally {
      loading.value = false
    }
  }

  return { data, error, loading, execute }
}
```

---

## 6. Réactivité avancée

```ts
// watch avec cleanup
watch(userId, async (newId, _, onCleanup) => {
  const controller = new AbortController()
  onCleanup(() => controller.abort())
  data.value = await fetchUser(newId, controller.signal)
})

// watchEffect — recalcul automatique sur toute dépendance lue
watchEffect(() => {
  document.title = `${route.name} | ${appName.value}`
})

// provide / inject (typage fort)
const ThemeKey: InjectionKey<Ref<string>> = Symbol('theme')
provide(ThemeKey, ref('dark'))
const theme = inject(ThemeKey)  // Ref<string> | undefined
```

---

## 7. Performance et code splitting

```ts
// Composant asynchrone avec loader/erreur
const HeavyChart = defineAsyncComponent({
  loader: () => import('@/components/HeavyChart.vue'),
  loadingComponent: Spinner,
  errorComponent: ErrorMsg,
  delay: 200,
  timeout: 5000,
})
```

- `v-once` sur contenus statiques coûteux.
- `v-memo="[dep1, dep2]"` pour listes avec rendu conditionnel lourd.
- Éviter les watchers larges sur `reactive()` — préférer des `watch` ciblés.

---

## 8. Tests avec Vitest + Vue Test Utils

```ts
// src/components/__tests__/Counter.spec.ts
import { mount } from '@vue/test-utils'
import Counter from '../Counter.vue'

test('émet increment au clic', async () => {
  const wrapper = mount(Counter, { props: { count: 3 } })
  await wrapper.find('button').trigger('click')
  expect(wrapper.emitted('increment')?.[0]).toEqual([4])
})
```

```bash
npx vitest run          # one-shot CI
npx vitest --ui         # interface graphique
```

---

## Garde-fous et anti-patterns

| Problème | Symptôme | Correction |
|----------|----------|-----------|
| Déstructuration directe d'un store | perte de réactivité | `storeToRefs()` |
| Mutation directe du state Pinia hors action | incohérence | passer par une action |
| Utilisation des mixins | conflits, opacité | composables `use*` |
| `reactive()` sur une ref | double-wrapping | utiliser `ref()` seul |
| Props muables localement | violation one-way | emit + event handler côté parent |
| Import de composants lourds sans lazy | bundle trop gros | `defineAsyncComponent` ou `() => import()` |
| `watch` sans nettoyage sur fetch | race condition | `onCleanup` + AbortController |
| `v-for` sans `:key` stable | rerenders erratiques | toujours `:key` sur id métier |

---

## Checklist qualité (2026)

- [ ] `<script setup lang="ts">` sur tous les composants
- [ ] Props typées via `defineProps<Interface>()`
- [ ] Emits déclarés avec `defineEmits<{ event: [type] }>()`
- [ ] Lazy loading sur toutes les routes
- [ ] Stores Pinia en setup stores (pas la syntaxe objet)
- [ ] Composables couverts par des tests unitaires
- [ ] `defineAsyncComponent` pour les composants > 20 KB
- [ ] Vue DevTools activé en dev pour profiler les rerenders


## Communication Rules — MANDATORY

- Ultra-concise. No filler, no preamble, no pleasantries.
- Never say "happy to help", "sure!", "great question", "let me", or similar.
- Tool first, talk second. Act before explaining.
- Result first. Lead with outcome, not process.
- Stop when done. No summary, no recap, no trailing commentary.
- No politeness wrappers. Direct and blunt.
- Minimum words. If one word works, do not use ten.
- No unsolicited explanations.
- No emoji unless asked.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.