# Nuxt 3: SSR i generowanie statyczne, kompletny przewodnik > Opanowanie SSR i generowania statycznego w Nuxt 3. Od useFetch po route rules: jak optymalizować wydajność aplikacji Vue.js. - Published: 2026-02-11 - Updated: 2026-04-28 - Author: SharpSkill - Tags: nuxt 3, vue.js, ssr, generowanie statyczne, wydajność web - Reading time: 12 min --- Nuxt 3 zmienia sposób tworzenia aplikacji Vue.js, oferując wiele trybów renderowania dostosowanych do różnych przypadków użycia. Od Server-Side Rendering (SSR) przez generowanie statyczne aż po renderowanie hybrydowe framework zapewnia wyjątkową elastyczność w optymalizacji wydajności i SEO. > **Wymagania wstępne** > > Ten tutorial zakłada podstawową znajomość Vue 3 i Composition API. Znajomość koncepcji renderowania po stronie serwera jest pomocna, lecz nie wymagana, ponieważ podstawy są wyjaśniane w trakcie przewodnika. ## Zrozumienie trybów renderowania w Nuxt 3 Zanim zacznie się pisać kod, warto zrozumieć różnice między dostępnymi trybami renderowania. Każdy tryb odpowiada na konkretne potrzeby związane z wydajnością, SEO i doświadczeniem użytkownika. SSR (Server-Side Rendering) generuje HTML na serwerze przy każdym żądaniu. Generowanie statyczne (SSG) wstępnie generuje wszystkie strony w czasie buildu. Tryb hybrydowy pozwala łączyć te podejścia strona po stronie. ```typescript // nuxt.config.ts // Konfiguracja różnych trybów renderowania export default defineNuxtConfig({ // SSR włączone domyślnie (zalecane dla SEO) ssr: true, // Generowanie statyczne: pre-renderuje wszystkie strony // Użyj 'npm run generate' do zbudowania // target: 'static', // Składnia Nuxt 2 // Tryb hybrydowy: konfigurowalny per trasa routeRules: { // Strona główna: pre-renderowana i cache'owana '/': { prerender: true }, // Blog: generowanie statyczne '/blog/**': { prerender: true }, // Dashboard: renderowanie tylko po stronie klienta '/dashboard/**': { ssr: false }, // API: bez pre-renderowania '/api/**': { prerender: false } } }) ``` Ta konfiguracja pokazuje moc trybu hybrydowego: każda sekcja aplikacji korzysta z trybu renderowania najlepiej dopasowanego do jej potrzeb. ## Pobieranie danych z useFetch i useAsyncData Nuxt 3 udostępnia dwa główne composable do izomorficznego pobierania danych. Działają one zarówno po stronie serwera, jak i klienta, z automatycznym zarządzaniem hydracją. `useFetch` to wrapper wokół `useAsyncData`, który upraszcza wywołania HTTP. `useAsyncData` daje większą kontrolę w zaawansowanych scenariuszach. ```vue ``` W przypadkach wymagających większej kontroli `useAsyncData` pozwala wykonać dowolną funkcję asynchroniczną. ```vue ``` Te composable zapobiegają podwójnemu pobieraniu: dane uzyskane na serwerze są serializowane w payloadzie HTML i ponownie wykorzystywane podczas hydracji po stronie klienta. ## Personalizacja SSR za pomocą server hooks SSR w Nuxt 3 można dostosować za pomocą server hooks. Hooki te pozwalają ingerować w różne etapy cyklu renderowania, aby modyfikować domyślne zachowanie. ```typescript // server/plugins/render-hooks.ts // Plugin serwera do dostosowywania renderowania SSR export default defineNitroPlugin((nitroApp) => { // Hook wykonywany przed renderowaniem każdej strony nitroApp.hooks.hook('render:html', (html, { event }) => { // Wstrzyknięcie skryptów lub metadanych html.head.push(` `) }) // Hook do zarządzania cache renderowania nitroApp.hooks.hook('render:response', (response, { event }) => { // Dodanie niestandardowych nagłówków cache const path = event.path if (path.startsWith('/blog/')) { // Długi cache dla artykułów blogowych response.headers['Cache-Control'] = 'public, max-age=3600, s-maxage=86400' } else if (path.startsWith('/api/')) { // Brak cache dla API response.headers['Cache-Control'] = 'no-store' } }) }) ``` > **Wydajność SSR** > > Hook `render:response` doskonale nadaje się do wdrażania strategii cache HTTP. Połączenie SSR z CDN, który respektuje nagłówki Cache-Control, pozwala serwować pre-renderowane strony zachowując możliwość ich unieważnienia. ## Generowanie statyczne z nuxt generate Generowanie statyczne buduje wszystkie strony z wyprzedzeniem w czasie buildu. Takie podejście jest idealne dla witryn ze stabilną treścią, takich jak blogi, dokumentacja czy strony marketingowe. W przypadku tras dynamicznych Nuxt musi znać wszystkie URL-e do wygenerowania. Hook `prerender:routes` pozwala definiować te trasy programowo. ```typescript // nuxt.config.ts // Pełna konfiguracja generowania statycznego export default defineNuxtConfig({ // Włączenie generowania statycznego nitro: { prerender: { // Włączenie automatycznego crawlowania linków crawlLinks: true, // Trasy zawsze do uwzględnienia routes: ['/', '/about', '/contact'], // Ignorowanie wybranych tras ignore: ['/admin', '/api'] } }, hooks: { // Hook do generowania tras dynamicznych async 'prerender:routes'(ctx) { // Pobranie artykułów z API lub bazy const articles = await fetch('https://api.example.com/articles') .then(res => res.json()) // Dodanie tras artykułów for (const article of articles) { ctx.routes.add(`/blog/${article.slug}`) } // Pobranie kategorii const categories = await fetch('https://api.example.com/categories') .then(res => res.json()) for (const category of categories) { ctx.routes.add(`/category/${category.slug}`) } } } }) ``` W projektach z dużą liczbą stron automatyczny crawler może okazać się niewystarczający. Oto bardziej solidne podejście z osobnym plikiem konfiguracyjnym. ```typescript // server/utils/generate-routes.ts // Narzędzie do generowania listy tras dynamicznych import { prisma } from './prisma' export async function getAllStaticRoutes(): Promise { const routes: string[] = [] // Artykuły blogowe const articles = await prisma.article.findMany({ where: { published: true }, select: { slug: true, category: { select: { slug: true } } } }) for (const article of articles) { routes.push(`/blog/${article.category.slug}/${article.slug}`) } // Strony produktów const products = await prisma.product.findMany({ where: { active: true }, select: { slug: true } }) for (const product of products) { routes.push(`/products/${product.slug}`) } // Strony tagów const tags = await prisma.tag.findMany({ select: { slug: true } }) for (const tag of tags) { routes.push(`/tags/${tag.slug}`) } return routes } ``` ## Renderowanie hybrydowe z routeRules Renderowanie hybrydowe jest sztandarową funkcją Nuxt 3. Pozwala definiować różne reguły renderowania dla każdej trasy, łącząc to, co najlepsze z SSR i SSG. ```typescript // nuxt.config.ts // Zaawansowana konfiguracja renderowania hybrydowego export default defineNuxtConfig({ routeRules: { // Strony marketingowe: pre-renderowane i długo cache'owane '/': { prerender: true }, '/pricing': { prerender: true }, '/features/**': { prerender: true }, // Blog: ISR (Incremental Static Regeneration) // Rewalidacja co godzinę '/blog/**': { isr: 3600, prerender: true }, // Dokumentacja: cache CDN z rewalidacją '/docs/**': { swr: 86400, // Stale-while-revalidate prerender: true }, // E-commerce: SSR z krótkim cache '/products/**': { ssr: true, cache: { maxAge: 60, staleMaxAge: 300 } }, // Koszyk i checkout: tylko po stronie klienta '/cart': { ssr: false }, '/checkout/**': { ssr: false }, // Dashboard: tryb SPA '/dashboard/**': { ssr: false, // Wyłączenie pre-renderowania prerender: false }, // Trasy API: brak cache domyślnie '/api/**': { cors: true, headers: { 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE' } } } }) ``` Ta konfiguracja ilustruje typową architekturę współczesnej aplikacji: strony publiczne są optymalizowane pod SEO za pomocą SSG, podczas gdy interaktywne sekcje używają renderowania po stronie klienta. ## Optymalizacja wydajności z cache danych Poza cache stron Nuxt 3 pozwala cache'ować pobrane dane. Strategia ta zmniejsza obciążenie API i poprawia czasy odpowiedzi. ```typescript // server/api/articles/[slug].get.ts // Endpoint API z cache danych import { getArticleBySlug } from '~/server/utils/articles' export default defineCachedEventHandler( async (event) => { const slug = getRouterParam(event, 'slug') if (!slug) { throw createError({ statusCode: 400, message: 'Brak slugu' }) } const article = await getArticleBySlug(slug) if (!article) { throw createError({ statusCode: 404, message: 'Artykuł nie znaleziony' }) } return article }, { // Klucz cache oparty na slugu getKey: (event) => `article-${getRouterParam(event, 'slug')}`, // Czas życia cache: 1 godzina maxAge: 3600, // Stale-while-revalidate: serwowanie nieaktualnego cache podczas aktualizacji staleMaxAge: 7200, // Unieważnianie oparte na tagach tags: ['articles'] } ) ``` Aby unieważnić cache przy zmianie treści, Nuxt udostępnia system tagów. ```typescript // server/api/articles/[slug].put.ts // Aktualizacja artykułu z unieważnieniem cache import { updateArticle } from '~/server/utils/articles' export default defineEventHandler(async (event) => { const slug = getRouterParam(event, 'slug') const body = await readBody(event) // Aktualizacja artykułu const article = await updateArticle(slug, body) // Unieważnienie cache dla tego artykułu await useStorage('cache').removeItem(`nitro:handlers:article-${slug}`) // Lub unieważnianie po tagach (wszystkie artykuły) // await useStorage('cache').clear('articles') return article }) ``` > **Cache rozproszony** > > W produkcji z wieloma instancjami cache w pamięci jest niewystarczający. Zaleca się skonfigurowanie Redisa lub innego systemu rozproszonego za pośrednictwem konfiguracji Nitro, aby zapewnić spójność między instancjami. ## Zarządzanie SEO i metadanymi SSR umożliwia optymalizację SEO poprzez generowanie metadanych po stronie serwera. Nuxt 3 oferuje kilka podejść do dynamicznego zarządzania meta tagami. ```vue ``` Dla stron statycznych metadane można zdefiniować bezpośrednio w komponencie. ```vue ``` ## Wdrożenie i kwestie produkcyjne Wybór metody wdrożenia zależy od używanego trybu renderowania. Oto główne opcje i ich konfiguracje. ```typescript // nuxt.config.ts // Konfiguracja dla różnych środowisk wdrożenia export default defineNuxtConfig({ nitro: { // Preset zależny od platformy docelowej // preset: 'vercel', // Vercel // preset: 'netlify', // Netlify // preset: 'cloudflare-pages', // Cloudflare // preset: 'node-server', // Klasyczny Node.js // Konfiguracja dla Node.js w produkcji preset: 'node-server', // Kompresja odpowiedzi compressPublicAssets: true, // Konfiguracja magazynu cache storage: { cache: { driver: 'redis', url: process.env.REDIS_URL } } }, // Zmienne środowiskowe runtime runtimeConfig: { // Sekrety (niedostępne dla klienta) apiSecret: process.env.API_SECRET, // Konfiguracja publiczna public: { apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api' } } }) ``` Dla wdrożenia statycznego polecenie `npm run generate` tworzy folder `.output/public` gotowy do wdrożenia na dowolnym hoście plików statycznych. ```bash # Generowanie statyczne npm run generate # Zawartość .output/public można wdrożyć na: # - Vercel (automatyczne wykrywanie) # - Netlify (automatyczna konfiguracja) # - GitHub Pages # - S3 + CloudFront # - Dowolnym CDN lub serwerze plików statycznych ``` ## Podsumowanie Nuxt 3 oferuje wyjątkową elastyczność w renderowaniu aplikacji Vue.js. Wybór między SSR, SSG i renderowaniem hybrydowym zależy od konkretnych potrzeb każdego projektu. **Kluczowe wnioski:** ✅ **SSR**: idealne dla treści dynamicznych wymagających dobrej SEO (e-commerce, serwisy informacyjne) ✅ **SSG**: doskonałe dla stabilnych treści (blogi, dokumentacja, strony marketingowe) ✅ **Hybrydowe**: najlepsze podejście dla złożonych aplikacji o różnych potrzebach ✅ **useFetch/useAsyncData**: automatyczna hydracja i zarządzanie cache ✅ **routeRules**: precyzyjna konfiguracja zachowania każdej trasy ✅ **Caching**: liczne strategie optymalizacji wydajności w produkcji Połączenie renderowania hybrydowego z dobrze przemyślaną strategią cache pozwala budować wydajne aplikacje zoptymalizowane pod SEO, zachowujące jednocześnie interaktywność Single Page Applications. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/vue-nuxt/nuxt-3-ssr-static-generation