Kurs Vue.js · Moduł 15: TypeScript i Nuxt

Koncepcje SSR: SPA vs SSR vs SSG vs ISR

3 min czytania
W tej lekcji6

Centrum Dowodzenia NOVA LAB musi dostarczać informacje możliwie najszybciej. Różne strategie renderowania są jak różne systemy łączności stacji - każdy ma swoje zalety w zależności od sytuacji.

SPA - Single Page Application

Tradycyjna aplikacja Vue to SPA (Single Page Application):

  1. Przeglądarka pobiera pusty HTML
  2. Ładuje JavaScript (bundle Vue)
  3. JavaScript renderuje całą stronę w przeglądarce
  4. Nawigacja odbywa się bez przeładowania strony
1Serwer → [pusty HTML + JS bundle] → Przeglądarka → [renderowanie]

Zalety SPA:

  • Płynna nawigacja bez przeładowań
  • Bogata interaktywność
  • Prosty hosting (statyczne pliki)

Wady SPA:

  • Wolne pierwsze ładowanie (duży JS bundle)
  • Pusta strona do czasu załadowania JS
  • Słabe SEO - boty widzą pusty HTML

SSR - Server-Side Rendering

SSR renderuje stronę na serwerze i wysyła gotowy HTML:

  1. Serwer otrzymuje request
  2. Vue renderuje komponent na serwerze do HTML
  3. Wysyła gotowy HTML do przeglądarki
  4. Przeglądarka wyświetla stronę natychmiast
  5. JavaScript jest ładowany i "hydratuje" stronę (dodaje interaktywność)
1Request → Serwer [renderuje HTML] → Przeglądarka [wyświetla + hydration]

Zalety SSR:

  • Szybsze First Contentful Paint (FCP)
  • Doskonałe SEO - boty widzą pełny HTML
  • Lepsza wydajność na wolnych urządzeniach

Wady SSR:

  • Większe obciążenie serwera
  • Dłuższy Time to Interactive (TTI)
  • Bardziej złożona infrastruktura

SSG - Static Site Generation

SSG generuje HTML w czasie budowania (build time):

  1. Podczas npm run build - każda strona jest prerenderowana do HTML
  2. Pliki HTML są gotowe do serwowania
  3. Brak serwera - zwykły hosting statyczny
  4. JavaScript hydratuje stronę na kliencie
1Build time → [generuje HTML] → Deploy → CDN → Przeglądarka

Zalety SSG:

  • Najszybsza możliwa odpowiedź (pliki z CDN)
  • Brak serwera - najtańsze
  • Doskonałe SEO

Wady SSG:

  • Treść nie zmienia się między buildami
  • Każda zmiana wymaga rebuildu
  • Nie nadaje się do dynamicznych danych

ISR - Incremental Static Regeneration

ISR łączy SSG i SSR - strony są generowane statycznie, ale mogą się odświeżać:

  1. Pierwsza wizyta - serwowana strona ze statycznego cache
  2. Po określonym czasie - strona jest regenerowana w tle
  3. Następna wizyta dostaje zaktualizowaną wersję
1Request → [cache/CDN] → jeśli stałe → regeneruj w tle → nowa wersja

Zalety ISR:

  • Szybkość SSG + świeżość SSR
  • Automatyczne odświeżanie

Wady ISR:

  • Bardziej skomplikowana konfiguracja
  • Dane mogą być chwilowo nieaktualne (stale-while-revalidate)

Hydration - klucz do interaktywności

Hydration to proces, w którym Vue "przejmuje" statyczny HTML wygenerowany przez serwer i dodaje do niego interaktywność:

1Serwer: <button>Kliknij</button>  ← statyczny HTML
2   ↓ hydration
3Klient: <button @click="handler">Kliknij</button>  ← interaktywny

Vue porównuje wirtualny DOM z istniejącym HTML i podłącza event handlery, ref-y i reaktywność - bez ponownego renderowania całego DOM.

Kiedy użyć jakiej strategii?

StrategiaUżyj gdy...
SPAAplikacja wymaga bogatej interaktywności (dashboard, edytor)
SSRWażne SEO + dynamiczne dane (sklep, blog z komentarzami)
SSGTreść zmienia się rzadko (dokumentacja, landing page)
ISRTreść zmienia się, ale nie w czasie rzeczywistym (blog, katalog)
Kod do tej lekcji: 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: 'Renderowanie w przegladarce po zaladowaniu JS',
21    pros: ['Plynna nawigacja', 'Bogata interaktywnosc', 'Prosty hosting'],
22    cons: ['Wolne ladowanie', 'Slabe SEO', 'Pusty HTML'],
23    useCase: 'Dashboard, edytor, aplikacja wewnetrzna',
24    color: '#00b4d8'
25  },
26  {
27    name: 'SSR',
28    fullName: 'Server-Side Rendering',
29    description: 'Serwer renderuje HTML i wysyla gotowa strone',
30    pros: ['Szybki FCP', 'Doskonale SEO', 'Lepsza wydajnosc'],
31    cons: ['Obciazenie serwera', 'Dluzszy TTI', 'Zlozona infrastruktura'],
32    useCase: 'Sklep, blog, social media',
33    color: '#00ff88'
34  },
35  {
36    name: 'SSG',
37    fullName: 'Static Site Generation',
38    description: 'HTML generowany w czasie budowania (build)',
39    pros: ['Najszybsza odpowiedz', 'Brak serwera', 'Doskonale SEO'],
40    cons: ['Statyczna tresc', 'Rebuild przy zmianie', 'Nie dla dynamicznych danych'],
41    useCase: 'Dokumentacja, landing page, portfolio',
42    color: '#9b59b6'
43  },
44  {
45    name: 'ISR',
46    fullName: 'Incremental Static Regeneration',
47    description: 'Statyczne strony odswiezane w tle',
48    pros: ['Szybkosc SSG + swiezosc SSR', 'Automatyczne odswiezanie'],
49    cons: ['Skomplikowana konfiguracja', 'Dane chwilowo stale'],
50    useCase: 'Blog, katalog, strona firmowa',
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>Strategie Renderowania</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">Zalety</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">Wady</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>Zastosowanie:</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>

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co oznacza SSR (Server-Side Rendering)?

  2. 2. Czym jest hydration (hydratacja) w kontekście SSR?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Zdefiniuj typ union RenderStrategy i interfejs StrategyInfo.

  • Układanie w pionie

    Uporządkuj kroki procesu Server-Side Rendering:

  • Edytor kodu

    Zdefiniuj interfejs CrewMember i typuj stany data, pending, error.

  • Układanie w pionie

    Ułóż kolejność ładowania w SPA (Single Page Application):

  • Klikanie w kolejności

    Ułóż strategie renderowania od najszybszego do najwolniejszego First Contentful Paint:

  • Edytor kodu

    Zdefiniuj typ union Page i typuj nawigację między stronami.

Przydatne artykuły