📦 Installation CDN & createRouter
Vue Router est la bibliothèque officielle de navigation pour Vue 3. Dans un contexte CDN (sans serveur, sans bundler), on utilise les versions IIFE chargées via <script>.
Chargement via CDN
<!-- Vue 3 en premier, puis Vue Router -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.js"></script>
<script>
// Destructurer depuis l'objet global VueRouter
const { createRouter, createWebHashHistory, RouterLink, RouterView,
useRouter, useRoute } = VueRouter;
const { createApp, ref, computed } = Vue;
</script>
Pourquoi createWebHashHistory en CDN ?
Il existe deux modes d'historique dans Vue Router :
createWebHistory()— URLs propres (/articles/1) mais nécessite une configuration serveur pour rediriger toutes les URLs versindex.htmlcreateWebHashHistory()— URLs avec hash (/#/articles/1) — tout se passe côté client, aucun serveur requis
createWebHashHistory(). Avec createWebHistory(), rafraîchir la page donnera une erreur 404.Création du router
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: AccueilPage },
{ path: '/about', component: AboutPage },
]
});
const app = createApp(AppRoot);
app.use(router); // enregistrer le plugin Vue Router
app.mount('#app');
app.use(router) enregistre globalement <RouterLink> et <RouterView> comme composants, et injecte useRouter() / useRoute() dans toute l'application.🗺️ Définir les routes
Chaque route est un objet avec au minimum path et component. On peut aussi lui donner un name pour naviguer sans dépendre du chemin.
Structure d'un tableau de routes
const routes = [
// Route simple
{ path: '/', component: HomePage },
// Route nommée
{ path: '/articles', name: 'articles', component: ArticlesPage },
// Route avec paramètre dynamique
{ path: '/articles/:id', name: 'article', component: ArticleDetailPage },
// Route imbriquée (children)
{
path: '/profil',
component: ProfilLayout,
children: [
{ path: '', component: ProfilInfos },
{ path: 'edit', component: ProfilEdit },
]
},
// Route 404 — doit être en dernier !
{ path: '/:pathMatch(.*)*', name: 'notFound', component: NotFoundPage },
];
Propriétés importantes d'une route
| Propriété | Type | Description |
|---|---|---|
path | string | Chemin URL. Commence toujours par / |
component | object | Le composant Vue à afficher |
name | string | Nom unique pour la navigation nommée |
meta | object | Données personnalisées (ex: { requiresAuth: true }) |
redirect | string/object | Rediriger vers un autre chemin |
children | array | Routes imbriquées |
Composants inline (CDN)
En CDN, les composants sont définis comme des objets avec une option template :
const HomePage = {
template: `
<div>
<h1>Accueil</h1>
<p>Bienvenue sur mon site !</p>
</div>
`
};
const AboutPage = {
template: `<div><h1>À propos</h1></div>`
};
🔗 RouterLink & RouterView
Ces deux composants sont les piliers de Vue Router côté template.
RouterView
<RouterView /> est le slot de rendu — il affiche le composant correspondant à la route active. On le place là où le contenu doit changer selon la navigation.
const AppRoot = {
// RouterView est enregistré globalement par app.use(router)
template: `
<div>
<nav>
<!-- RouterLink : navigation déclarative -->
<RouterLink to="/">Accueil</RouterLink>
<RouterLink to="/articles">Articles</RouterLink>
<RouterLink to="/about">À propos</RouterLink>
</nav>
<main>
<!-- Le composant de la route active s'affiche ici -->
<RouterView />
</main>
</div>
`
};
RouterLink vs <a href>
| RouterLink | a href |
|---|---|
| Navigation sans rechargement de page | Recharge toute la page |
Ajoute automatiquement la classe router-link-active | Pas de classe active |
| Prend en compte le mode history/hash | URL brute uniquement |
| Compatible avec les routes nommées | Chemin brut uniquement |
RouterLink avancé
<!-- Vers une route nommée -->
<RouterLink :to="{ name: 'article', params: { id: 42 } }">Lire</RouterLink>
<!-- Avec query string -->
<RouterLink :to="{ path: '/articles', query: { page: 2 } }">Page 2</RouterLink>
<!-- Classe active personnalisée -->
<RouterLink to="/about" active-class="mon-lien-actif">À propos</RouterLink>
router-link-active est ajoutée si le chemin commence par le to. router-link-exact-active est ajoutée seulement si le chemin est exactement identique.🔧 Paramètres de route
Les paramètres dynamiques permettent de créer des routes génériques comme /article/:id où :id capture n'importe quelle valeur.
Définir un paramètre
const routes = [
{ path: '/article/:id', component: ArticleDetail },
{ path: '/user/:username/posts/:postId', component: UserPost },
];
Lire les paramètres avec useRoute()
const ArticleDetail = {
setup() {
const route = useRoute();
// Paramètre de route — /article/42 => params.id === "42"
const articleId = computed(() => route.params.id);
// Query string — /article/42?lang=fr => query.lang === "fr"
const langue = computed(() => route.query.lang || 'fr');
// Hash — /article/42#commentaires => hash === "#commentaires"
const section = computed(() => route.hash);
return { articleId, langue, section };
},
template: `
<div>
<h1>Article #{{ articleId }}</h1>
<p>Langue : {{ langue }}</p>
</div>
`
};
Réagir aux changements de paramètre
Si on navigue de /article/1 à /article/2, le composant est réutilisé (pas détruit/recréé). Il faut observer les changements :
const { watch, ref } = Vue;
const ArticleDetail = {
setup() {
const route = useRoute();
const article = ref(null);
// Watcher sur route.params pour réagir aux changements
watch(() => route.params.id, (newId) => {
chargerArticle(newId);
}, { immediate: true }); // immediate: true pour le chargement initial
async function chargerArticle(id) {
// Simuler un fetch
article.value = { id, titre: `Article numéro ${id}` };
}
return { article };
},
template: `
<div v-if="article">
<h1>{{ article.titre }}</h1>
</div>
`
};
route.params.id est toujours une chaîne — si vous avez besoin d'un nombre, utilisez Number(route.params.id).⚡ Navigation programmatique
Parfois on veut naviguer depuis du code JavaScript (après un login, après soumission d'un formulaire...) plutôt que depuis un <RouterLink>. On utilise alors useRouter().
useRouter() — méthodes principales
const MonComposant = {
setup() {
const router = useRouter();
function allerAccueil() {
// Naviguer vers un chemin
router.push('/');
}
function allerProfil(userId) {
// Naviguer avec un objet (route nommée + params)
router.push({ name: 'profil', params: { id: userId } });
}
function allerRechercheAvecFiltres(q) {
// Naviguer avec query string
router.push({ path: '/recherche', query: { q, page: 1 } });
}
function remplacerSansHistorique() {
// replace() : ne pas ajouter à l'historique (le bouton "précédent" ne revient pas)
router.replace('/dashboard');
}
function retour() {
router.go(-1); // Équivalent de window.history.back()
}
function avancer() {
router.go(1); // Équivalent de window.history.forward()
}
return { allerAccueil, allerProfil, allerRechercheAvecFiltres,
remplacerSansHistorique, retour, avancer };
},
template: `
<div>
<button @click="allerAccueil">Accueil</button>
<button @click="retour">← Retour</button>
</div>
`
};
push() vs replace()
| Méthode | Historique | Cas d'usage |
|---|---|---|
router.push() | Ajoute une entrée | Navigation normale |
router.replace() | Remplace l'entrée actuelle | Login → Dashboard (on ne veut pas revenir en arrière) |
router.go(n) | Navigation relative | Boutons précédent/suivant |
🛡️ Navigation Guards
Les guards interceptent chaque navigation avant qu'elle ne se produise. Ils permettent de protéger des routes, de journaliser, ou de faire des redirections conditionnelles.
Guard global — beforeEach
// Enregistrer un guard APRÈS la création du router
router.beforeEach((to, from) => {
// to : la route de destination
// from : la route de départ
console.log(`Navigation : ${from.path} → ${to.path}`);
// Retourner true (ou rien) : la navigation continue
// Retourner false : la navigation est annulée
// Retourner une route : redirection
return true;
});
Routes protégées avec meta.requiresAuth
// Marquer une route comme protégée via meta
const routes = [
{ path: '/', component: HomePage },
{ path: '/login', component: LoginPage },
{
path: '/dashboard',
component: DashboardPage,
meta: { requiresAuth: true } // ← méta-donnée personnalisée
},
{
path: '/admin',
component: AdminPage,
meta: { requiresAuth: true, role: 'admin' }
},
];
// Guard global qui vérifie l'authentification
const estAuthentifie = ref(false); // En pratique : lire depuis un store Pinia
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !estAuthentifie.value) {
// Rediriger vers login en mémorisant la destination
return { path: '/login', query: { redirect: to.fullPath } };
}
// Sinon laisser passer
});
afterEach — journalisation
// afterEach : s'exécute APRÈS chaque navigation réussie
router.afterEach((to, from) => {
// Pas de contrôle sur la navigation (trop tard)
// Utile pour : analytics, scroll-to-top, title de page
document.title = to.meta.title || 'Mon Application';
window.scrollTo(0, 0);
});
beforeEach reçoivent aussi un 3ème paramètre next() en ancienne API. En Vue Router 4 avec Composition API, retourner une valeur est suffisant — next() est facultatif.