Які режими рендерингу в Nuxt (SSR, SSG, SPA, Hybrid)?
Режими рендерингу Nuxt визначають де і коли генерується HTML: на сервері при кожному запиті (SSR), заздалегідь під час збірки (SSG), у браузері клієнта (SPA), або по-різному для кожного роута (Hybrid).
Теорія
TL;DR
- SSR = кухня ресторану, яка готує страву під кожне замовлення. SSG = страви приготовані заздалегідь і чекають на видачу. SPA = продукти привезли додому і готуєш сам. Hybrid = для кожної страви свій підхід.
- Головний поділ: SSR і SSG віддають готовий HTML з сервера. SPA надсилає JS-бандл, а браузер збирає сторінку самостійно.
- SEO + свіжі дані? SSR. Статичний контент + CDN? SSG. Дашборд за логіном? SPA. Змішаний сайт? Hybrid через
routeRules. - В основі всіх чотирьох режимів - Nitro, серверний рушій Nuxt.
Швидкий приклад
// nuxt.config.ts - чотири режими в одному файлі
export default defineNuxtConfig({
// SSR (за замовчуванням): сервер рендерить HTML при кожному запиті
ssr: true,
// SSG: запусти `npx nuxi generate` - отримаєш папку /dist зі статичними HTML-файлами
// ssr: true + команда nuxi generate
// SPA: браузер робить все сам, сервер віддає порожню оболонку
// ssr: false
// Hybrid (Nuxt 3): правила рендерингу по роутах
routeRules: {
'/blog/**': { ssr: true }, // SSR для SEO
'/dashboard/**': { ssr: false } // SPA для інтерактивності
}
})Запусти npx nuxi dev з ssr: true і побачиш серверні логи при кожному переході на сторінку. Переключи на ssr: false і логи зникнуть. Браузер тепер сам з усім розбирається.
Ключова різниця
SSR і SSG обидва генерують справжній HTML, який одразу отримують і пошукові краулери, і користувачі. Відмінність у часі: SSR генерує HTML у момент запиту, SSG - один раз під час збірки. SPA взагалі не генерує HTML на сервері. Він надсилає JS-бандл, і браузер мусить завантажити, розібрати та виконати JavaScript перш ніж з'явиться будь-який контент. Ця затримка шкодить і SEO, і відчуттю швидкості.
Коли що використовувати
- SEO + контент що часто оновлюється: SSR. Новинні сайти, сторінки товарів e-commerce, все де дані міняються щогодини.
- Статичний контент, швидкість CDN: SSG. Документація, маркетингові сторінки, лендінги. Сам nuxt.com побудований через
nuxi generate. - Інтерактивний додаток без SEO: SPA. Адмін-панелі, дашборди, все що за авторизацією.
- Змішаний сайт (блог + адмін): Hybrid через
routeRules. Роути блогу отримують SSR, адмін - SPA. - Персоналізовані дані: SSR або Hybrid, де
useAsyncDataабоuseFetchвиконується на сервері.
Таблиця порівняння
| Параметр | SSR | SSG | SPA | Hybrid |
|---|---|---|---|---|
| Доставка HTML | Сервер при запиті | Статичні файли зі збірки | Порожня оболонка + JS | По-різному для кожного роута |
| SEO | Відмінне | Відмінне | Погане (залежить від JS) | Залежить від роута |
| TTFB | Середній (обчислення сервера) | Найшвидший (CDN edge) | Повільний (завантаження + виконання JS) | Оптимізовано для кожного роута |
| Динамічні дані | Повна підтримка (useAsyncData) | Тільки під час збірки | Запити з клієнта | Змішано |
| Час збірки | Швидко | Зростає з кількістю сторінок | Найшвидше | Як SSR + статичні роути |
| Витрати на сервер | Високі (кожен запит) | Нуль (CDN) | Нуль після деплою | Низькі (тільки SSR-роути) |
| Підходить для | E-commerce, блоги | Документація, лендінги | Адмін-панелі | Більшість реальних проектів |
Як Nitro це обробляє
Nitro, серверний рушій Nuxt, компілює Vue-компоненти в серверні рендерери для SSR і SSG. У режимі SSR Nitro виконує useAsyncData і useFetch в Node.js при кожному запиті, серіалізує результат в HTML і додає JSON-пейлоад у теги <script> для клієнтської гідрації. SSG робить те саме, але один раз при збірці і записує .html-файли. Режим SPA повністю оминає рендерер Nitro. Vite збирає бандл для браузера. Hybrid-режим аналізує routeRules і маршрутизує SSR-шляхи до обробників Nitro, а SPA-шляхи до клієнтської точки входу.
Повна послідовність для Hybrid-запиту до /blog/post: запит потрапляє до Nitro, збігається з правилом ssr: true, Vue рендериться у рядок HTML через @vue/server-renderer, виконується useAsyncData в Node, JSON-пейлоад додається в <script>, відповідь стримується клієнту.
Типові помилки
1. ssr: false на сайті що потребує SEO.
// Неправильно: краулери отримують порожній div
export default defineNuxtConfig({ ssr: false })
// curl http://localhost:3000 → <div id="__nuxt"></div>
// Правильно: тримай SSR і перевіряй через curl
export default defineNuxtConfig({ ssr: true })
// curl http://localhost:3000 → <article>Повний текст поста</article>2. useFetch у SSG для зовнішніх API, яких нема під час збірки.
// Неправильно: збірка впаде, бо API недоступне в момент nuxi generate
const { data } = await useFetch('/api/live-prices')
// Правильно: виконуй тільки на клієнті
const { data } = await useFetch('/api/live-prices', { server: false })3. Клієнт-only компоненти без <ClientOnly> на SSR-роутах.
Компонент, який звертається до window або document, зламає серверний рендеринг. Огорни його:
<ClientOnly>
<HeavyChartComponent />
</ClientOnly>4. nuxi build замість nuxi generate для SSG-деплою.
nuxi build створює SSR-сервер на Node.js. nuxi generate виводить статичні HTML-файли для CDN. Неправильна команда - неправильний тип виводу і зламаний деплой.
5. Очікування що SSG обробить невідомі динамічні параметри.
Якщо під час збірки згенеровано /users/1, /users/2, /users/3, запит до /users/99 поверне 404. SSG фіксує тільки ті сторінки, які знає при збірці. Для всіх можливих ID правильний вибір - SSR.
Де зустрічається в реальних проектах
- nuxt.com використовує SSG через
nuxi generate, задеплоєний на CDN. - Vercel автоматично розпізнає SSG-роути з
routeRulesу Hybrid-деплоях. - E-commerce сайти застосовують SSR для кошика і checkout, SSG для каталогу товарів.
- Nuxt + Contentful використовують SSR-блоги з
useAsyncData. - Адмін-панелі отримують
ssr: falseна роутах/admin/**.
Питання на співбесіді
Q: Чим Hybrid у Nuxt 3 відрізняється від universal mode у Nuxt 2?
A: Nuxt 2 мав єдину глобальну SSR + клієнтська гідрація модель. У Nuxt 3 через routeRules можна вибирати режим для кожного роута окремо: SSR для /blog/** і SPA-поведінку для /admin/** в одному додатку без жодного глобального перемикача.
Q: Що відбувається з useState у SSG?
A: При збірці стан ініціалізується на сервері звично. Після гідрації на клієнті useState зберігається в пам'яті браузера. Але оскільки сервера при рантаймі нема, стан не переживає перезавантаження сторінки так само, як в SSR.
Q: Як симулювати ISR у Nuxt?
A: Нативного ISR, як у Next.js, у Nuxt нема. Найближче рішення - routeRules з swr: 3600 (stale-while-revalidate): сторінка подається з кешу миттєво, а в фоні відбувається ревалідація. Для повноцінної схеми з вебхук-перезбірками потрібно комбінувати prerender: true з CI-тригером.
Q: Як дебажити повільний SSR time-to-interactive?
A: Профілюй Nitro через dev tools, використовуй useLazyFetch для некритичних даних щоб перший HTML прийшов швидше, і огортай важкі сторонні компоненти у <ClientOnly> щоб не блокувати серверний рендеринг.
Q: Що в Hybrid-режимі визначає чи використовує роут SSR або SPA? Відстеж повну послідовність.
A: Правила routeRules у nuxt.config.ts. Запит потрапляє до Nitro, збігається з патерном шляху, і якщо правило ssr: false - Nitro повертає клієнтську HTML-оболонку. Якщо ssr: true - Vue рендериться через @vue/server-renderer, useAsyncData виконується в Node, JSON-пейлоад вбудовується у <script>, повна HTML-відповідь стримується клієнту.
Приклади
SSR-сторінка блогу з серверним завантаженням даних
<!-- pages/blog/[slug].vue -->
<template>
<article>
<h1>{{ post.title }}</h1>
<div v-html="post.body" />
</article>
</template>
<script setup>
const route = useRoute()
// useAsyncData виконується на сервері - краулери бачать повний контент
const { data: post } = await useAsyncData('post', () =>
$fetch(`/api/posts/${route.params.slug}`)
)
</script>Сервер виконує $fetch до /api/posts/[slug] ще до відправки HTML клієнту. Якщо переглянути код сторінки, заголовок і тіло статті вже в розмітці - JavaScript їх не вставляє пізніше.
Hybrid-додаток: блог і адмін-панель
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/blog/**': { ssr: true }, // Повний SSR: SEO + свіжі дані
'/dashboard/**': { ssr: false }, // SPA: краулери не потрібні, багата інтерактивність
'/': { prerender: true } // SSG головна: максимально швидкий TTFB
}
})<!-- pages/dashboard/analytics.vue - виконується тільки на клієнті -->
<script setup>
const { data: stats } = await useFetch('/api/analytics', { server: false })
</script>Три різних поведінки в одному Nuxt-додатку. Головна сторінка - статичний файл на CDN. Пости блогу - SSR з Node-сервера. Дашборд - SPA-оболонка, яка завантажує свої дані після монтування.
E-commerce з ISR-подібним кешуванням
// nuxt.config.ts - реальне налаштування e-commerce
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }, // Статична головна сторінка
'/products': { swr: 600 }, // Каталог: кеш 10 хв, ревалідація у фоні
'/products/**': { swr: 3600 }, // Сторінки товарів: кеш 1 година
'/cart': { ssr: false }, // SPA кошик: персональний, SEO не потрібен
'/checkout': { ssr: true }, // SSR checkout: безпека + персоналізація
'/admin/**': { ssr: false } // SPA адмін-панель
}
})swr (stale-while-revalidate) відразу віддає закешований контент і запускає ревалідацію у фоні після закінчення кеш-вікна. Сторінки товарів завантажуються швидко з кешу і при цьому залишаються достатньо актуальними. Це максимально близько до ISR у Next.js без зовнішніх тригерів перезбірки.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.