Aller au contenu

Structure d'un projet Vue

Slides

Deck 6 — Structure d'un projet Vue (PDF)PDF

Objectif : faire le tour du propriétaire d'un projet fraîchement généré, savoir à quoi sert chaque fichier, et comprendre le chemin que suit le navigateur pour afficher ta page.

Tu viens de lancer npm create vue@latest, puis npm install, et VS Code t'affiche une bonne vingtaine de fichiers. Rassure-toi : tu vas en toucher trois, et connaître les autres de loin suffit largement.

L'arborescence

Arborescence d'un projet Vue affichée dans l'explorateur de VS Code

Voici ce que génère npm create vue@latest en 2026, avec les options Linter (ESLint + Oxlint) et Prettier cochées (le dossier node_modules/, créé par npm install, n'est pas montré : il contient des milliers de fichiers que tu ne liras jamais) :

mon-portfolio/
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── public/
│   └── favicon.ico
├── src/
│   ├── assets/
│   │   ├── base.css
│   │   ├── logo.svg
│   │   └── main.css
│   ├── components/
│   │   ├── icons/
│   │   │   ├── IconCommunity.vue
│   │   │   ├── IconDocumentation.vue
│   │   │   ├── IconEcosystem.vue
│   │   │   ├── IconSupport.vue
│   │   │   └── IconTooling.vue
│   │   ├── HelloWorld.vue
│   │   ├── TheWelcome.vue
│   │   └── WelcomeItem.vue
│   ├── App.vue
│   └── main.js
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .oxlintrc.json
├── .prettierrc.json
├── eslint.config.js
├── index.html
├── jsconfig.json
├── package.json
├── README.md
└── vite.config.js

Note

Les versions bougent vite dans cet écosystème. Si ton projet contient un fichier de plus ou de moins que la liste ci-dessus, ce n'est pas grave : les fichiers importants, eux, sont toujours là.

Les quatre fichiers que tu vas vraiment ouvrir

index.html, src/main.js, src/App.vue et package.json. On les regarde un par un.

index.html : la page unique

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

Deux lignes comptent :

  • <div id="app"></div> est la cible. Vue va remplacer le contenu de ce div vide par toute ton application.
  • <script type="module" src="/src/main.js"></script> est le déclencheur : il charge ton JavaScript, qui démarre Vue.

Le reste est du HTML classique. Tu ne reviendras dans ce fichier que pour changer le <title>, la langue (lang="fr") et le favicon.

src/main.js : le démarrage

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

Quatre lignes, quatre rôles :

  • import './assets/main.css' charge les styles globaux de l'application.
  • import { createApp } from 'vue' récupère la fonction qui fabrique une application Vue.
  • import App from './App.vue' récupère ton composant racine.
  • createApp(App).mount('#app') crée l'application à partir de App.vue et l'installe dans le <div id="app"> de index.html.

En une phrase : « prends App.vue, transforme-le en application, et colle-le dans #app ».

src/App.vue : le composant racine

C'est le sommet de ton application : tout le reste est appelé depuis lui. Voici ce que génère Vue par défaut, en version raccourcie :

<script setup>
  import HelloWorld from './components/HelloWorld.vue'
  import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />
    </div>
  </header>

  <main>
    <TheWelcome />
  </main>
</template>

<style scoped>
  header {
    line-height: 1.5;
  }

  .logo {
    display: block;
    margin: 0 auto 2rem;
  }
</style>

Un fichier .vue se lit en trois blocs :

  • <script setup> : la logique du composant, et les imports des composants enfants.
  • <template> : le HTML affiché. Les balises à majuscule (<HelloWorld />, <TheWelcome />) sont des composants, pas des balises HTML.
  • <style scoped> : le CSS. Le mot scoped limite ce style à ce composant uniquement — il ne fuit pas dans le reste du site.

Ce contenu est une démonstration. Tu vas le vider dès l'exercice à la fin de ce chapitre.

package.json : la carte d'identité du projet

{
  "name": "mon-portfolio",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "run-s \"lint:*\"",
    "lint:oxlint": "oxlint . --fix",
    "lint:eslint": "eslint . --fix --cache",
    "format": "prettier --write --experimental-cli src/"
  },
  "dependencies": {
    "vue": "^3.5.42"
  },
  "devDependencies": {
    "@eslint/js": "^10.0.1",
    "@vitejs/plugin-vue": "^6.0.8",
    "eslint": "^10.10.0",
    "eslint-config-prettier": "^10.1.8",
    "eslint-plugin-oxlint": "~1.82.0",
    "eslint-plugin-vue": "~10.11.0",
    "globals": "^17.12.0",
    "npm-run-all2": "^9.0.3",
    "oxlint": "~1.82.0",
    "prettier": "3.9.6",
    "vite": "^8.2.2",
    "vite-plugin-vue-devtools": "^8.2.1",
    "vue-eslint-parser": "^10.4.1"
  },
  "engines": {
    "node": "^22.18.0 || >=24.12.0"
  }
}

Les métadonnées, d'abord :

  • name : le nom du projet, repris du dossier que tu as créé.
  • private: true : empêche une publication accidentelle sur npm.
  • type: "module" : le projet utilise les modules modernes (import / export).
  • engines : les versions de Node.js compatibles. Si ton Node est plus ancien, npm t'avertira.

Les scripts, ensuite. Tu les lances avec npm run <nom> :

Commande Ce qu'elle fait
npm run dev Démarre le serveur de développement sur http://localhost:5173. C'est celle que tu tapes tous les jours.
npm run build Fabrique la version optimisée du site dans un dossier dist/. C'est ce dossier qui part en ligne.
npm run preview Sert le contenu de dist/ en local, pour vérifier le résultat final avant mise en ligne.
npm run lint Relit tout le code et corrige les problèmes qu'il sait corriger seul.
npm run format Remet le code de src/ en forme avec Prettier.

Enfin, les dépendances :

  • dependencies : ce dont le site a besoin pour fonctionner une fois en ligne. Ici, une seule ligne : Vue.
  • devDependencies : les outils qui ne servent que pendant que tu développes — Vite, ESLint, Prettier, le plugin Vue. Ils ne finissent pas dans le site publié.

Astuce

Le ^ devant un numéro de version signifie « cette version ou une plus récente, tant que le premier chiffre ne change pas ». ^3.5.42 accepte donc 3.6.0 mais jamais 4.0.0.

Les fichiers de configuration

Tu ne les ouvriras presque jamais, mais autant savoir qui est qui.

vite.config.js

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
})

Vite est l'outil qui sert ton site pendant le développement et qui le compile pour la mise en ligne. Deux choses à retenir ici :

  • plugins : le plugin vue() apprend à Vite à lire les fichiers .vue ; vueDevTools() ajoute un petit panneau de débogage flottant dans la page.
  • alias : le raccourci @ désigne le dossier src/. Tu peux donc écrire import Bouton from '@/components/Bouton.vue' depuis n'importe où, au lieu de compter les ../../.

C'est le fichier jsconfig.json qui fait connaître ce même raccourci @ à VS Code, pour que l'autocomplétion suive.

Note

C'est dans ce fichier que tu ajouteras le plugin Tailwind au chapitre suivant. Tu ajouteras deux lignes, tu ne supprimeras rien.

eslint.config.js et .oxlintrc.json

La configuration du linter : les règles qui soulignent les erreurs dans ton éditeur. Tu n'as rien à y faire pour l'instant.

.prettierrc.json

Les préférences de mise en forme du code :

{
  "$schema": "https://json.schemastore.org/prettierrc",
  "semi": false,
  "singleQuote": true,
  "printWidth": 100
}

Pas de point-virgule en fin de ligne, des guillemets simples, des lignes de 100 caractères maximum. Prettier applique ça à chaque sauvegarde.

.vscode/extensions.json

{
  "recommendations": [
    "Vue.volar",
    "dbaeumer.vscode-eslint",
    "EditorConfig.EditorConfig",
    "oxc.oxc-vscode",
    "esbenp.prettier-vscode"
  ]
}

Ce fichier est une liste d'extensions recommandées pour ce projet. Quand tu ouvres le dossier, VS Code affiche une notification « Ce dossier recommande des extensions » et te propose de les installer d'un clic. Pratique quand on travaille à plusieurs sur un même projet : tout le monde finit avec le même outillage. Le fichier voisin .vscode/settings.json fait la même chose pour les réglages (Format On Save y est déjà activé).

public/ ou src/assets/ : où mettre mes images ?

La question revient toujours. La règle est simple.

src/assets/ : les fichiers que tu importes depuis un composant (une image de ta galerie, une police, un CSS). Vite les prend en charge : il les optimise, les compresse et renomme le fichier final. C'est le choix par défaut pour presque tout.

public/ : les fichiers copiés tels quels, sans transformation, accessibles à la racine du site. Utilise ce dossier quand l'URL doit rester exactement celle que tu écris : favicon.ico, robots.txt, un PDF de CV à télécharger.

Un fichier public/cv.pdf sera servi à l'adresse /cv.pdf. Ce dossier est public au sens propre : n'y range rien de confidentiel.

Le flux d'exécution

Tu tapes npm run dev, et voici l'enchaînement :

  1. Vite démarre un serveur local sur http://localhost:5173.
  2. Le navigateur demande la page et reçoit index.html.
  3. index.html contient un <div id="app"> vide et charge /src/main.js.
  4. main.js importe le CSS global, crée l'application avec App.vue, et la monte dans #app.
  5. App.vue affiche son <template> et appelle ses composants enfants (HelloWorld, TheWelcome…).
  6. Chaque composant produit son HTML, et Vue assemble le tout dans la page.

En une ligne : index.html → main.js → App.vue → composants enfants → ta page.

Et tant que le serveur tourne, chaque fois que tu sauvegardes un fichier, Vite recharge instantanément la partie modifiée dans le navigateur. Tu n'as jamais besoin de rafraîchir à la main.

Exercice : fais le ménage

La page de démonstration de Vue est jolie, mais ce n'est pas ton portfolio. On repart d'une page blanche. Cet état de départ sera le point de départ des chapitres sur Tailwind : fais-le sérieusement.

À faire :

  1. Supprime les composants de démo : src/components/HelloWorld.vue, TheWelcome.vue, WelcomeItem.vue et tout le dossier src/components/icons/.
  2. Supprime aussi src/assets/logo.svg.
  3. Vide src/App.vue et remplace son contenu par un simple <h1>Mon portfolio</h1>.
  4. Vide src/assets/main.css et src/assets/base.css (garde les fichiers, mais laisse-les vides ou avec deux ou trois règles de base).
  5. Dans index.html, passe lang="fr" et change le <title> en « Mon portfolio ».
  6. Lance npm run dev : la page doit afficher un h1 « Mon portfolio », rien d'autre, et la console du navigateur doit être vide d'erreurs.

src/App.vue :

<script setup>
  // Rien pour l'instant : on ajoutera nos composants ici.
</script>

<template>
  <h1>Mon portfolio</h1>
</template>

<style scoped>
  h1 {
    text-align: center;
  }
</style>

src/assets/base.css — quelques règles minimales suffisent :

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: system-ui, sans-serif;
  line-height: 1.6;
}

src/assets/main.css — on garde uniquement l'import de base.css :

@import './base.css';

index.html :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mon portfolio</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

src/main.js ne change pas : il importe toujours ./assets/main.css, qui existe toujours.

En cas d'erreur : si la page affiche « Failed to resolve import ./components/HelloWorld.vue », c'est qu'un import vers un composant supprimé traîne encore dans App.vue. Relis ton <script setup>.

En résumé

  • index.html : la page unique, avec <div id="app"> comme cible.
  • src/main.js : crée l'application Vue et la monte dans #app.
  • src/App.vue : le composant racine, sommet de l'arbre.
  • src/components/ : tes composants réutilisables.
  • src/assets/ : ce qui est importé et optimisé ; public/ : ce qui est copié tel quel.
  • package.json : les scripts (dev, build, preview, lint, format) et les dépendances.
  • Flux : index.html → main.js → App.vue → composants.

Le projet est vide et propre. On peut commencer à le remplir.