Nuxt 3: SSR і статична генерація, повний посібник
Опанувати SSR і статичну генерацію з Nuxt 3. Від useFetch до route rules: як оптимізувати продуктивність застосунків Vue.js.

Nuxt 3 змінює спосіб створення застосунків Vue.js, пропонуючи кілька режимів рендерингу, що пасують до різних сценаріїв. Від Server-Side Rendering (SSR) до статичної генерації та гібридного рендерингу — фреймворк надає виняткову гнучкість для оптимізації продуктивності та SEO.
Цей посібник передбачає базові знання Vue 3 та Composition API. Знайомство з концепціями серверного рендерингу буде корисним, але не обов'язковим, оскільки основи пояснюються по ходу матеріалу.
Розуміння режимів рендерингу Nuxt 3
Перед тим як занурюватися в код, важливо зрозуміти відмінності між доступними режимами рендерингу. Кожен режим відповідає на конкретні потреби щодо продуктивності, SEO та користувацького досвіду.
SSR (Server-Side Rendering) генерує HTML на сервері для кожного запиту. Статична генерація (SSG) попередньо генерує всі сторінки на етапі збірки. Гібридний режим дозволяє поєднувати ці підходи посторінково.
// Конфігурація різних режимів рендерингу
export default defineNuxtConfig({
// SSR увімкнено за замовчуванням (рекомендовано для SEO)
ssr: true,
// Статична генерація: попередньо рендерить усі сторінки
// Використовуйте 'npm run generate' для збірки
// target: 'static', // Синтаксис Nuxt 2
// Гібридний режим: налаштовується для кожного маршруту
routeRules: {
// Головна сторінка: попередньо рендериться та кешується
'/': { prerender: true },
// Блог: статична генерація
'/blog/**': { prerender: true },
// Dashboard: рендеринг лише на клієнті
'/dashboard/**': { ssr: false },
// API: без попереднього рендерингу
'/api/**': { prerender: false }
}
})Ця конфігурація демонструє силу гібридного режиму: кожна частина застосунку використовує найбільш відповідний для своїх потреб режим рендерингу.
Отримання даних із useFetch і useAsyncData
Nuxt 3 надає два основні composables для ізоморфного отримання даних. Вони працюють як на сервері, так і на клієнті, з автоматичним керуванням гідратацією.
useFetch — це обгортка над useAsyncData, що спрощує HTTP-виклики. useAsyncData дає більше контролю в розширених сценаріях.
<script setup lang="ts">
// pages/blog/[slug].vue
// Сторінка деталей статті з useFetch
// Отримання параметра маршруту
const route = useRoute()
// useFetch: автоматичне отримання даних
// Дані отримуються на сервері та гідратуються на клієнті
const { data: article, pending, error } = await useFetch(
`/api/articles/${route.params.slug}`,
{
// Унікальний ключ для кешу та дедуплікації
key: `article-${route.params.slug}`,
// Перетворення даних за потреби
transform: (response) => response.data,
// Опції кешу
getCachedData: (key) => {
// Перевірити, чи є дані в кеші
const nuxtApp = useNuxtApp()
return nuxtApp.payload.data[key]
}
}
)
// Обробка помилок із навігацією
if (error.value) {
throw createError({
statusCode: 404,
message: 'Статтю не знайдено'
})
}
</script>
<template>
<div>
<div v-if="pending" class="loading">
Завантаження статті...
</div>
<article v-else-if="article">
<h1>{{ article.title }}</h1>
<div v-html="article.content" />
</article>
</div>
</template>Для випадків, що потребують більшого контролю, useAsyncData дозволяє виконати будь-яку асинхронну функцію.
<script setup lang="ts">
// pages/products/index.vue
// Список продуктів з useAsyncData та фільтрами
const route = useRoute()
// useAsyncData: повний контроль над логікою отримання даних
const { data: products, refresh } = await useAsyncData(
'products-list',
async () => {
// Отримання з кількох джерел за потреби
const [productsResponse, categoriesResponse] = await Promise.all([
$fetch('/api/products', {
query: {
category: route.query.category,
sort: route.query.sort || 'date'
}
}),
$fetch('/api/categories')
])
// Об'єднання та перетворення даних
return {
products: productsResponse.data,
categories: categoriesResponse.data,
total: productsResponse.meta.total
}
},
{
// Оновлення при зміні query params
watch: [() => route.query]
}
)
// Функція ручного оновлення
const updateFilters = async (newCategory: string) => {
await navigateTo({
query: { ...route.query, category: newCategory }
})
}
</script>Ці composables запобігають подвійному отриманню даних: дані, отримані на сервері, серіалізуються в HTML-payload і повторно використовуються під час гідратації на клієнті.
Налаштування SSR за допомогою server hooks
SSR у Nuxt 3 можна налаштувати через server hooks. Ці хуки дозволяють втручатися на різних етапах циклу рендерингу, щоб змінити стандартну поведінку.
// Серверний плагін для кастомізації SSR-рендерингу
export default defineNitroPlugin((nitroApp) => {
// Хук, що виконується перед рендерингом кожної сторінки
nitroApp.hooks.hook('render:html', (html, { event }) => {
// Інжекція скриптів або метаданих
html.head.push(`
<script>
// Аналітика або глобальна конфігурація
window.__APP_CONFIG__ = {
environment: '${process.env.NODE_ENV}',
apiUrl: '${process.env.API_URL}'
}
</script>
`)
})
// Хук для керування кешем рендерингу
nitroApp.hooks.hook('render:response', (response, { event }) => {
// Додавання кастомних заголовків кешу
const path = event.path
if (path.startsWith('/blog/')) {
// Тривалий кеш для статей блогу
response.headers['Cache-Control'] = 'public, max-age=3600, s-maxage=86400'
} else if (path.startsWith('/api/')) {
// Без кешу для API
response.headers['Cache-Control'] = 'no-store'
}
})
})Хук render:response ідеально підходить для впровадження стратегій HTTP-кешу. Поєднання SSR із CDN, який поважає заголовки Cache-Control, дозволяє віддавати попередньо відрендерені сторінки, зберігаючи можливість їх інвалідації.
Статична генерація з nuxt generate
Статична генерація заздалегідь будує всі сторінки на етапі збірки. Цей підхід ідеальний для сайтів зі стабільним контентом — блогів, документації або маркетингових сайтів.
Для динамічних маршрутів Nuxt має знати всі URL, які треба згенерувати. Хук prerender:routes дозволяє визначати ці маршрути програмно.
// Повна конфігурація для статичної генерації
export default defineNuxtConfig({
// Увімкнути статичну генерацію
nitro: {
prerender: {
// Увімкнути автоматичне сканування посилань
crawlLinks: true,
// Маршрути, які завжди потрібно включати
routes: ['/', '/about', '/contact'],
// Ігнорувати певні маршрути
ignore: ['/admin', '/api']
}
},
hooks: {
// Хук для генерації динамічних маршрутів
async 'prerender:routes'(ctx) {
// Отримання статей з API або БД
const articles = await fetch('https://api.example.com/articles')
.then(res => res.json())
// Додавання маршрутів статей
for (const article of articles) {
ctx.routes.add(`/blog/${article.slug}`)
}
// Отримання категорій
const categories = await fetch('https://api.example.com/categories')
.then(res => res.json())
for (const category of categories) {
ctx.routes.add(`/category/${category.slug}`)
}
}
}
})Для проєктів із багатьма сторінками автоматичний crawler може бути недостатнім. Ось більш надійний підхід з окремим конфігураційним файлом.
// Утиліта для генерації списку динамічних маршрутів
import { prisma } from './prisma'
export async function getAllStaticRoutes(): Promise<string[]> {
const routes: string[] = []
// Статті блогу
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}`)
}
// Сторінки продуктів
const products = await prisma.product.findMany({
where: { active: true },
select: { slug: true }
})
for (const product of products) {
routes.push(`/products/${product.slug}`)
}
// Сторінки тегів
const tags = await prisma.tag.findMany({
select: { slug: true }
})
for (const tag of tags) {
routes.push(`/tags/${tag.slug}`)
}
return routes
}Готовий до співбесід з Vue.js / Nuxt.js?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Гібридний рендеринг із routeRules
Гібридний рендеринг — флагманська функція Nuxt 3. Він дозволяє визначати різні правила рендерингу для кожного маршруту, поєднуючи переваги SSR та SSG.
// Розширена конфігурація гібридного рендерингу
export default defineNuxtConfig({
routeRules: {
// Маркетингові сторінки: попередньо відрендерені та довго кешовані
'/': { prerender: true },
'/pricing': { prerender: true },
'/features/**': { prerender: true },
// Блог: ISR (Incremental Static Regeneration)
// Ревалідація щогодини
'/blog/**': {
isr: 3600,
prerender: true
},
// Документація: CDN-кеш з ревалідацією
'/docs/**': {
swr: 86400, // Stale-while-revalidate
prerender: true
},
// E-commerce: SSR з коротким кешем
'/products/**': {
ssr: true,
cache: {
maxAge: 60,
staleMaxAge: 300
}
},
// Кошик і checkout: лише на клієнті
'/cart': { ssr: false },
'/checkout/**': { ssr: false },
// Dashboard: режим SPA
'/dashboard/**': {
ssr: false,
// Вимкнути попередній рендеринг
prerender: false
},
// API маршрути: без кешу за замовчуванням
'/api/**': {
cors: true,
headers: {
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE'
}
}
}
})Ця конфігурація ілюструє типову архітектуру сучасного застосунку: публічні сторінки оптимізуються для SEO за допомогою SSG, тоді як інтерактивні розділи використовують клієнтський рендеринг.
Оптимізація продуктивності з кешем даних
Окрім кешу сторінок, Nuxt 3 дозволяє кешувати отримані дані. Ця стратегія знижує навантаження на API та покращує час відповіді.
// API-ендпоінт із кешем даних
import { getArticleBySlug } from '~/server/utils/articles'
export default defineCachedEventHandler(
async (event) => {
const slug = getRouterParam(event, 'slug')
if (!slug) {
throw createError({
statusCode: 400,
message: 'Відсутній slug'
})
}
const article = await getArticleBySlug(slug)
if (!article) {
throw createError({
statusCode: 404,
message: 'Статтю не знайдено'
})
}
return article
},
{
// Ключ кешу на основі slug
getKey: (event) => `article-${getRouterParam(event, 'slug')}`,
// Тривалість кешу: 1 година
maxAge: 3600,
// Stale-while-revalidate: віддавати застарілий кеш під час оновлення
staleMaxAge: 7200,
// Інвалідація на основі тегів
tags: ['articles']
}
)Для інвалідації кешу при зміні контенту Nuxt надає систему тегів.
// Оновлення статті з інвалідацією кешу
import { updateArticle } from '~/server/utils/articles'
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')
const body = await readBody(event)
// Оновлення статті
const article = await updateArticle(slug, body)
// Інвалідація кешу для цієї статті
await useStorage('cache').removeItem(`nitro:handlers:article-${slug}`)
// Або інвалідація на основі тегів (всі статті)
// await useStorage('cache').clear('articles')
return article
})У продакшені з кількома інстансами кеш у пам'яті недостатній. Рекомендується налаштувати Redis або іншу розподілену систему через конфігурацію Nitro, щоб забезпечити узгодженість між інстансами.
Керування SEO та метаданими
SSR дозволяє оптимізувати SEO, генеруючи метадані на сервері. Nuxt 3 пропонує кілька підходів до динамічного керування meta tag'ами.
<script setup lang="ts">
// pages/blog/[slug].vue
// Сторінка блогу з оптимізованим SEO
const route = useRoute()
const { data: article } = await useFetch(`/api/articles/${route.params.slug}`)
// Динамічна SEO-конфігурація на основі статті
useSeoMeta({
title: article.value?.title,
description: article.value?.excerpt,
ogTitle: article.value?.title,
ogDescription: article.value?.excerpt,
ogImage: article.value?.coverImage,
ogType: 'article',
twitterCard: 'summary_large_image',
twitterTitle: article.value?.title,
twitterDescription: article.value?.excerpt,
twitterImage: article.value?.coverImage
})
// Структуровані дані для Google
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Article',
headline: article.value?.title,
description: article.value?.excerpt,
image: article.value?.coverImage,
datePublished: article.value?.publishedAt,
dateModified: article.value?.updatedAt,
author: {
'@type': 'Organization',
name: 'SharpSkill'
}
})
}
]
})
</script>Для статичних сторінок метадані можна визначати безпосередньо в компоненті.
<script setup lang="ts">
// pages/about.vue
// Статична сторінка з SEO
definePageMeta({
title: 'Про нас'
})
useSeoMeta({
title: 'Про SharpSkill | Підготовка до технічних співбесід',
description: 'Знайомство з SharpSkill — платформою підготовки до технічних співбесід. Місія: допомагати розробникам успішно проходити технічні співбесіди.',
ogTitle: 'Про SharpSkill',
ogDescription: 'Платформа підготовки до технічних співбесід',
ogImage: '/images/og-about.webp'
})
</script>Розгортання та виробничі міркування
Вибір способу розгортання залежить від обраного режиму рендерингу. Ось основні варіанти та їхні конфігурації.
// Конфігурація для різних середовищ розгортання
export default defineNuxtConfig({
nitro: {
// Preset відповідно до цільової платформи
// preset: 'vercel', // Vercel
// preset: 'netlify', // Netlify
// preset: 'cloudflare-pages', // Cloudflare
// preset: 'node-server', // Класичний Node.js
// Конфігурація для Node.js у продакшені
preset: 'node-server',
// Стиснення відповідей
compressPublicAssets: true,
// Конфігурація сховища кешу
storage: {
cache: {
driver: 'redis',
url: process.env.REDIS_URL
}
}
},
// Змінні середовища під час виконання
runtimeConfig: {
// Секрети (не доступні клієнту)
apiSecret: process.env.API_SECRET,
// Публічна конфігурація
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
}
}
})Для статичного розгортання команда npm run generate створює папку .output/public, готову до розміщення на будь-якому хостингу статичних файлів.
# Статична генерація
npm run generate
# Вміст .output/public можна розгорнути на:
# - Vercel (автоматичне визначення)
# - Netlify (автоматична конфігурація)
# - GitHub Pages
# - S3 + CloudFront
# - Будь-якому CDN або сервері статичних файлівПочинай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Висновок
Nuxt 3 пропонує виняткову гнучкість для рендерингу застосунків Vue.js. Вибір між SSR, SSG та гібридним рендерингом залежить від конкретних потреб кожного проєкту.
Ключові висновки:
✅ SSR: ідеально для динамічного контенту, що потребує гарного SEO (e-commerce, новинні сайти)
✅ SSG: чудово для стабільного контенту (блоги, документація, маркетингові сайти)
✅ Гібридний: найкращий підхід для складних застосунків з різноманітними потребами
✅ useFetch/useAsyncData: автоматична гідратація та керування кешем
✅ routeRules: точне налаштування поведінки кожного маршруту
✅ Кешування: множина стратегій для оптимізації продуктивності в продакшені
Поєднання гібридного рендерингу з продуманою стратегією кешування дозволяє створювати продуктивні застосунки, оптимізовані для SEO, зберігаючи інтерактивність Single Page Applications.
Теги
Поділитися
Пов'язані статті

Ключові запитання співбесід із Vue.js: 25 запитань, щоб отримати роботу
Підготуйтеся до співбесід із Vue.js, маючи в арсеналі 25 ключових запитань. Від реактивності до composables — опануйте найважливіше для наступної зустрічі.

Nuxt 4 у 2026 році: Нова структура каталогів та міграція з Nuxt 3
Повний посібник з міграції на Nuxt 4: нова структура app/, singleton-шар отримання даних, поверхнева реактивність за замовчуванням, розділення TypeScript-контексту, нормалізовані імена компонентів, Vue Router v5, зміни в управлінні head та контрольний список міграції.

Vue 3 Pinia vs Vuex: Сучасне управління станом та питання для співбесід 2026
Порівняння Pinia та Vuex: архітектура, TypeScript, Composition API, міграція, гідратація SSR та найпоширеніші питання зі співбесід щодо управління станом Vue у 2026 році.