Nuxt.js: Vue-фреймворк для серверного рендеринга и не только

 Nuxt.js — серверный рендеринг для Vue-приложений. Сервер генерирует готовую HTML-страницу и отправляет её в браузер.

При разработке на Vue.js рано или поздно встаёт вопрос: как сделать так, чтобы страницы индексировались поисковиками, загружались мгновенно и при этом не превращались в монструозный SPA с тонной JavaScript на клиенте? Ответ - Nuxt.js. Это open-source фреймворк, построенный поверх Vue.js, который решает проблему серверного рендеринга, маршрутизации и SEO "из коробки". Если вы уже знакомы с основами Vue.js, разобраться с Nuxt будет несложно - он построен на тех же принципах, но добавляет мощные абстракции поверх.

Зачем нужен серверный рендеринг

Обычное Vue-приложение работает по принципу CSR (Client-Side Rendering): браузер получает практически пустой HTML-файл, затем скачивает и выполняет JavaScript-бандл, и только после этого Vue рендерит интерфейс. Пользователь в это время смотрит на белый экран.

Проблемы CSR:

  • Поисковые роботы могут не дождаться отрисовки и не проиндексировать контент
  • Первая загрузка страницы медленная - особенно на мобильных устройствах и плохом интернете
  • Социальные сети не видят Open Graph-разметку - ссылки на ваш сайт в мессенджерах и соцсетях выглядят уныло, без превью

SSR (Server-Side Rendering) выглядит иначе: Vue-компоненты рендерятся в HTML на сервере, браузер получает полностью готовую страницу, и пользователь видит контент сразу. После загрузки JavaScript происходит гидратация - Vue "оживляет" статическую разметку, навешивая обработчики событий и делая страницу интерактивной. О том, как Vue управляет DOM-деревом через механизм Virtual DOM, мы рассказывали в отдельной статье.

Что даёт SSR:

  • SEO: краулеры Google, Яндекс и Bing получают готовый HTML и индексируют его без проблем
  • Скорость: показатель First Contentful Paint (FCP) минимален - контент виден сразу
  • Доступность: страница читается скринридерами до загрузки JS
  • Социальные превью: Open Graph и Twitter Cards работают корректно

Минусы тоже есть: нужен сервер для рендеринга, нельзя использовать browser-only API в произвольном месте кода, и сама разработка чуть сложнее, чем в чистом SPA. Но Nuxt.js берёт большую часть этой боли на себя.

Nuxt также поддерживает статическую генерацию (SSG) и гибридный рендеринг - но к этому мы вернёмся позже.

Сравнение схем рендеринга: CSR (Client-Side Rendering) — браузер получает пустой HTML и ждёт загрузки JavaScript для отображения контента; SSR (Server-Side Rendering) — сервер отдаёт готовый HTML, контент виден сразу, затем происходит гидратация.

Установка и структура проекта

Создать проект проще простого:

npm create nuxt@latest my-app

После пары вопросов (нужен ли TypeScript, ESLint, Pinia и т.д.) получаем готовую структуру:

my-app/
  app/
    pages/        # файловая маршрутизация
    components/   # автоимпортируемые компоненты
    composables/  # переиспользуемая логика
    layouts/      # шаблоны страниц
    middleware/   # защита роутов
    assets/       # стили, изображения
  server/
    api/          # серверные эндпоинты
  nuxt.config.ts  # конфигурация

Точка входа - app/app.vue. В отличие от обычного Vue-проекта, здесь нет main.js: Nuxt сам создаёт и конфигурирует приложение. Минимальный app.vue выглядит так:

<template>
  <div>
    <NuxtPage />
  </div>
</template>

<NuxtPage /> - это место, куда рендерится текущая страница. Всё, можно запускать npm run dev и открывать http://localhost:3000.

Файловая маршрутизация

Одна из главных фишек Nuxt - роутинг на основе файловой структуры. Не нужно вручную настраивать Vue Router: создаёте файл в app/pages/, и он автоматически становится маршрутом.

app/pages/
  index.vue     →  /
  about.vue     →  /about
  blog/
    [slug].vue  →  /blog/:slug

Динамические параметры - в квадратных скобках. Значение параметра можно получить через useRoute():

<script setup lang="ts">
const route = useRoute()
console.log(route.params.slug) // для /blog/my-post → 'my-post'
</script>

Для навигации используется <NuxtLink> - он работает как <RouterLink>, но с автоматическим префетчингом: когда ссылка попадает во viewport, Nuxt заранее подгружает компонент страницы и данные. Переход происходит мгновенно.

<template>
  <NuxtLink to="/about">О проекте</NuxtLink>
</template>

Для защиты страниц есть route middleware. Например, закрываем админку от неавторизованных:

// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (!isAuthenticated()) {
    return navigateTo('/login')
  }
})
<script setup lang="ts">
definePageMeta({ middleware: 'auth' })
</script>

Получение данных: useFetch и useAsyncData

В обычном Vue вы бы написали fetch внутри onMounted. В Nuxt это создаст проблему: данные не попадут в серверный рендеринг, поисковик увидит пустую страницу, а на клиенте данные загрузятся повторно.

Nuxt решает это композаблами useFetch и useAsyncData. Они выполняют запрос на сервере, сохраняют результат в payload и передают его на клиент - без повторной загрузки.

<script setup lang="ts">
const { data: posts, status } = await useFetch('/api/posts')
</script>

<template>
  <div v-if="status === 'pending'">Загрузка...</div>

  <div v-else>
    <article v-for="post in posts" :key="post.id">
      <h2>{{ post.title }}</h2>
    </article>
  </div>
</template>

useFetch подходит для большинства случаев. Если API нестандартное или запросов несколько - используйте useAsyncData:

<script setup lang="ts">
const { data } = await useAsyncData('blog-data', async () => {
  const [posts, categories] = await Promise.all([
    $fetch('/api/posts'),
    $fetch('/api/categories'),
  ])
  return { posts, categories }
})
</script>

Полезные опции:

  • lazy: true - не блокирует навигацию, пока данные грузятся (страница откроется сразу, данные подтянутся позже)
  • server: false - запрос только на клиенте (для не-SEO-чувствительных данных)
  • transform - обработка данных перед сохранением в payload
  • watch - перезапрос при изменении реактивных зависимостей

SEO и мета-теги

SSR даёт свободный доступ к управлению <head> - и Nuxt делает это максимально удобно через композабл useHead или его типобезопасную версию useSeoMeta:

<script setup lang="ts">
useSeoMeta({
  title: 'Nuxt.js: серверный рендеринг для Vue',
  description: 'Разбираем Nuxt.js: SSR, маршрутизация и SEO-оптимизация',
  ogTitle: 'Nuxt.js: серверный рендеринг для Vue',
  ogDescription: 'Как превратить Vue-приложение в SEO-дружественный сайт',
  ogImage: 'https://mysite.com/og-image.png',
  twitterCard: 'summary_large_image',
})
</script>

Для страниц блога мета-теги должны быть динамическими - зависеть от данных поста. Это решается просто:

<script setup lang="ts">
const route = useRoute()

const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)

useSeoMeta({
  title: post.value?.title,
  description: post.value?.excerpt,
  ogImage: post.value?.coverImage,
})
</script>

Глобальные настройки (favicon, язык, title-шаблон) задаются в nuxt.config.ts:

export default defineNuxtConfig({
  app: {
    head: {
      htmlAttrs: { lang: 'ru' },
      link: [{ rel: 'icon', href: '/favicon.ico' }],
    },
  },
})

titleTemplate позволяет автоматически добавлять название сайта к заголовкам страниц - '%s — Мой Блог'.

Гибридный рендеринг: SSR + SSG в одном проекте

Уникальная возможность Nuxt - задавать стратегию рендеринга для отдельных маршрутов через routeRules:

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },               // главная генерится на билде
    '/blog/**': { swr: 3600 },              // кэш на сервере, перегенерация в фоне
    '/admin/**': { ssr: false },            // клиентский рендеринг для админки
  },
})

Это даёт лучшее из двух миров: статические страницы для максимальной скорости и SSR для динамического контента - и всё в одном приложении.

Схема гибридного рендеринга Nuxt: три стратегии в одном приложении — статические страницы генерируются на билде (prerender), страницы блога кэшируются на сервере и перестраиваются в фоне (swr: 3600), админ-панель рендерится только на клиенте (ssr: false).

Деплой

Nuxt через движок Nitro собирает приложение в универсальный .output/:

npm run build
node .output/server/index.mjs

Можно задеплоить куда угодно:

  • Node.js-сервер: стандартный пресет, PM2 для управления процессами
  • Статический хостинг: npm run generate - получите HTML-файлы для Netlify, GitHub Pages и т.д.
  • Edge-платформы: Vercel, Cloudflare Workers, Deno Deploy - через NITRO_PRESET

Заключение

Nuxt.js превращает Vue из клиентской библиотеки в полноценный full-stack инструмент. SSR решает проблемы с SEO и начальной загрузкой, файловый роутинг убирает рутину, а useFetch снимает головную боль с дублированием запросов.

Nuxt стоит выбрать, если вы делаете контентный сайт, блог, интернет-магазин или любой проект, где важна индексация и скорость первой загрузки. Если же у вас внутренняя админка или SPA без требований к SEO - хватит и обычного Vue.

💬 А вы используете SSR в своих проектах? Делитесь в комментариях - какой фреймворк выбрали и почему.

Предыдущая статья
WebSockets: протокол двусторонней связи в реальном времени между браузером и сервером - изометрическая иллюстрация с потоком данныхWebSocket: как работает реальное время в вебе
Нет комментариев
Ваш комментарий
Ваш адрес email не будет опубликован.
Обязательные поля помечены *
0
05 июн. 2026