Cours Vue.js · Module 15: TypeScript et Nuxt

Concepts SSR : SPA vs SSR vs SSG vs ISR

4 min de lecture
Dans cette leçon6

Le Centre de Commandement NOVA LAB doit transmettre les informations le plus rapidement possible. Les différentes stratégies de rendu sont comme les différents systèmes de communication de la station - chacune a ses avantages selon la situation.

SPA - Single Page Application

Une application Vue classique est une SPA (Single Page Application) :

  1. Le navigateur télécharge un fichier HTML vide
  2. Il charge le JavaScript (le bundle Vue)
  3. Le JavaScript rend toute la page dans le navigateur
  4. La navigation se fait sans rechargement de page
1Serveur → [HTML vide + bundle JS] → Navigateur → [rendu]

Avantages de la SPA :

  • Navigation fluide sans rechargements
  • Interactivité riche
  • Hébergement simple (fichiers statiques)

Inconvénients de la SPA :

  • Chargement initial lent (bundle JS volumineux)
  • Page vide jusqu'au chargement du JS
  • Mauvais référencement (SEO) - les robots voient un HTML vide

SSR - Server-Side Rendering

Le SSR rend la page sur le serveur et envoie du HTML prêt à l'emploi :

  1. Le serveur reçoit une requête
  2. Vue rend le composant en HTML sur le serveur
  3. Il envoie le HTML prêt au navigateur
  4. Le navigateur affiche la page immédiatement
  5. Le JavaScript est chargé et « hydrate » la page (ajoute l'interactivité)
1Requête → Serveur [rend le HTML] → Navigateur [affichage + hydratation]

Avantages du SSR :

  • First Contentful Paint (FCP) plus rapide
  • Excellent référencement (SEO) - les robots voient le HTML complet
  • Meilleures performances sur les appareils lents

Inconvénients du SSR :

  • Charge serveur plus élevée
  • Time to Interactive (TTI) plus long
  • Infrastructure plus complexe

SSG - Static Site Generation

Le SSG génère le HTML au moment du build :

  1. Pendant npm run build - chaque page est prérendue en HTML
  2. Les fichiers HTML sont prêts à être servis
  3. Aucun serveur nécessaire - simple hébergement statique
  4. Le JavaScript hydrate la page côté client
1Build time → [génère le HTML] → Déploiement → CDN → Navigateur

Avantages du SSG :

  • Réponse la plus rapide possible (fichiers depuis un CDN)
  • Aucun serveur - l'option la moins chère
  • Excellent référencement (SEO)

Inconvénients du SSG :

  • Le contenu ne change pas entre deux builds
  • Chaque modification nécessite un rebuild
  • Inadapté aux données dynamiques

ISR - Incremental Static Regeneration

L'ISR combine SSG et SSR - les pages sont générées statiquement mais peuvent se rafraîchir :

  1. Première visite - la page est servie depuis le cache statique
  2. Après un délai défini - la page est régénérée en arrière-plan
  3. La visite suivante reçoit la version mise à jour
1Requête → [cache/CDN] → si périmé → régénération en arrière-plan → nouvelle version

Avantages de l'ISR :

  • Vitesse du SSG + fraîcheur du SSR
  • Rafraîchissement automatique

Inconvénients de l'ISR :

  • Configuration plus complexe
  • Les données peuvent être momentanément périmées (stale-while-revalidate)

L'hydratation - la clé de l'interactivité

L'hydratation est le processus par lequel Vue « reprend le contrôle » du HTML statique généré par le serveur et y ajoute l'interactivité :

1Serveur : <button>Click</button>  ← HTML statique
2   ↓ hydratation
3Client : <button @click="handler">Click</button>  ← interactif

Vue compare le DOM virtuel au HTML existant et attache les gestionnaires d'événements, les refs et la réactivité - sans refaire le rendu de tout le DOM.

Quand utiliser quelle stratégie ?

StratégieÀ utiliser quand...
SPAL'app demande une interactivité riche (dashboard, éditeur)
SSRLe SEO compte + données dynamiques (boutique, blog avec commentaires)
SSGLe contenu change rarement (documentation, landing page)
ISRLe contenu change, mais pas en temps réel (blog, catalogue)
Code de cette leçon : App.vue
1<script setup lang="ts">
2import { ref } from 'vue'
3
4type RenderStrategy = 'SPA' | 'SSR' | 'SSG' | 'ISR'
5
6interface StrategyInfo {
7  name: RenderStrategy
8  fullName: string
9  description: string
10  pros: string[]
11  cons: string[]
12  useCase: string
13  color: string
14}
15
16const strategies = ref<StrategyInfo[]>([
17  {
18    name: 'SPA',
19    fullName: 'Single Page Application',
20    description: 'Rendering in the browser after JS loads',
21    pros: ['Navigation fluide', 'Interactivité riche', 'Hébergement simple'],
22    cons: ['Slow loading', 'Weak SEO', 'Empty HTML'],
23    useCase: 'Dashboard, éditeur, application interne',
24    color: '#00b4d8'
25  },
26  {
27    name: 'SSR',
28    fullName: 'Server-Side Rendering',
29    description: 'Le serveur rend le HTML et envoie une page prête',
30    pros: ['FCP rapide', 'Excellent SEO', 'Meilleures performances'],
31    cons: ['Charge serveur', 'TTI plus long', 'Infrastructure complexe'],
32    useCase: 'Boutique, blog, réseaux sociaux',
33    color: '#00ff88'
34  },
35  {
36    name: 'SSG',
37    fullName: 'Static Site Generation',
38    description: 'HTML généré au moment du build',
39    pros: ['Fastest response', 'No server', 'Excellent SEO'],
40    cons: ['Static content', 'Rebuild on change', 'Not for dynamic data'],
41    useCase: 'Documentation, landing page, portfolio',
42    color: '#9b59b6'
43  },
44  {
45    name: 'ISR',
46    fullName: 'Incremental Static Regeneration',
47    description: 'Pages statiques rafraîchies en arrière-plan',
48    pros: ['Vitesse du SSG + fraîcheur du SSR', 'Rafraîchissement automatique'],
49    cons: ['Complex configuration', 'Temporarily stale data'],
50    useCase: 'Blog, catalogue, site vitrine',
51    color: '#f39c12'
52  }
53])
54
55const selected = ref<RenderStrategy>('SPA')
56
57const current = ref<StrategyInfo>(strategies.value[0])
58
59function selectStrategy(name: RenderStrategy) {
60  selected.value = name
61  current.value = strategies.value.find(s => s.name === name)!
62}
63</script>
64
65<template>
66  <div class="nova-lab">
67    <h1>Stratégies de rendu</h1>
68
69    <div class="tabs">
70      <button
71        v-for="s in strategies"
72        :key="s.name"
73        @click="selectStrategy(s.name)"
74        :class="{ active: selected === s.name }"
75        :style="{ borderColor: s.color }"
76      >
77        {{ s.name }}
78      </button>
79    </div>
80
81    <div class="detail" :style="{ borderColor: current.color }">
82      <h2 :style="{ color: current.color }">{{ current.name }}</h2>
83      <p class="fullname">{{ current.fullName }}</p>
84      <p class="desc">{{ current.description }}</p>
85
86      <div class="lists">
87        <div>
88          <h4 style="color: #00ff88">Avantages</h4>
89          <ul>
90            <li v-for="pro in current.pros" :key="pro">{{ pro }}</li>
91          </ul>
92        </div>
93        <div>
94          <h4 style="color: #e74c3c">Inconvénients</h4>
95          <ul>
96            <li v-for="con in current.cons" :key="con">{{ con }}</li>
97          </ul>
98        </div>
99      </div>
100
101      <div class="use-case">
102        <strong>Utilisation :</strong> {{ current.useCase }}
103      </div>
104    </div>
105  </div>
106</template>
107
108<style scoped>
109.nova-lab {
110  background: #0a0e27;
111  color: #00ff88;
112  min-height: 100vh;
113  padding: 20px;
114  font-family: monospace;
115}
116h1 { color: #00ff88; margin-bottom: 15px; }
117h2 { margin-bottom: 4px; }
118.tabs { display: flex; gap: 8px; margin-bottom: 15px; }
119.tabs button {
120  background: rgba(0,180,216,0.1);
121  color: #ccc;
122  border: 2px solid #333;
123  padding: 10px 20px;
124  border-radius: 8px;
125  cursor: pointer;
126  font-family: monospace;
127  font-weight: bold;
128}
129.tabs button.active {
130  background: rgba(0,180,216,0.25);
131  color: white;
132}
133.detail {
134  background: rgba(0,180,216,0.06);
135  border: 2px solid #00b4d8;
136  padding: 20px;
137  border-radius: 12px;
138}
139.fullname { color: #888; font-size: 13px; margin-bottom: 8px; }
140.desc { color: #ccc; margin-bottom: 12px; }
141.lists { display: flex; gap: 20px; margin-bottom: 12px; }
142.lists ul { list-style: none; padding: 0; }
143.lists li { padding: 3px 0; font-size: 13px; color: #ccc; }
144.lists li::before { content: '> '; color: #00b4d8; }
145h4 { margin-bottom: 6px; }
146.use-case {
147  background: rgba(0,255,136,0.08);
148  padding: 10px 14px;
149  border-radius: 8px;
150  font-size: 13px;
151  color: #00b4d8;
152}
153</style>

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que signifie le SSR (Server-Side Rendering) ?

  2. 2. Qu'est-ce que l'hydratation dans le contexte du SSR ?

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Définis un type union RenderStrategy et une interface StrategyInfo.

  • Tri vertical

    Ordonne les étapes du processus de Server-Side Rendering :

  • Éditeur de code

    Définis une interface CrewMember et type les états data, pending, error.

  • Tri vertical

    Ordonne l'ordre de chargement d'une SPA (Single Page Application) :

  • Clique dans l'ordre

    Ordonne les stratégies de rendu du First Contentful Paint le plus rapide au plus lent :

  • Éditeur de code

    Définis un type union Page et type la navigation entre les pages.