Nuxt 4 для фронтенд-разработчиков: от основы до продакшена

Курс для разработчиков, знакомых с HTML/CSS/JS и базово с Vue, который помогает быстро начать работать с Nuxt 4. Разберём архитектуру, роутинг, данные, производительность, деплой и практики разработки для реальных проектов.

1. Введение в Nuxt 4 и архитектура приложения

Введение в Nuxt 4 и архитектура приложения

Что такое Nuxt 4

Nuxt 4 — это фреймворк поверх Vue, который помогает строить производительные и масштабируемые веб‑приложения с удобной архитектурой, готовыми решениями для роутинга, рендеринга, работы с сервером и деплоя.

Nuxt решает типовые задачи, которые в «чистом» Vue обычно приходится собирать вручную:

  • Файловый роутинг без ручной настройки маршрутов
  • SSR (рендеринг на сервере) и SSG (генерация статических страниц)
  • Удобная работа с данными: серверные API‑роуты, прокси/бэкенд‑эндпоинты, кеширование
  • Автоподключение компонентов и composables
  • Единый способ подключать плагины, middleware и конфигурацию окружений
  • Официальные источники для дальнейшего чтения:

  • Документация Nuxt
  • Документация Vue
  • Документация Nitro
  • Почему Nuxt — это не просто «Vue + роутер»

    Важно понимать архитектурную идею Nuxt: приложение одновременно может быть клиентским и серверным.

  • На клиенте работает Vue‑приложение (интерактивность, события, реактивность).
  • На сервере (или edge/runtime) Nuxt может подготовить HTML заранее (SSR/SSG), отдать API‑ответы и выполнить часть логики ближе к пользователю.
  • В результате вы получаете:

  • Быстрый первый рендер (особенно при SSR/SSG)
  • Улучшенную индексацию страниц (SEO) для контентных страниц
  • Единый проект, где фронтенд и небольшой серверный слой живут рядом
  • Режимы рендеринга в Nuxt

    Nuxt поддерживает несколько стратегий, и выбор влияет на архитектуру и производительность.

  • SSR (Server-Side Rendering): сервер генерирует HTML на каждый запрос, затем страница «оживает» на клиенте.
  • SSG (Static Site Generation): страницы генерируются заранее во время сборки и раздаются как статические файлы.
  • SPA (Single Page Application): рендеринг в основном на клиенте, сервер отдаёт «пустую оболочку» и JS.
  • Ключевой термин: гидратация — это процесс, когда уже отрендеренный HTML от сервера связывается с клиентским JavaScript, чтобы страница стала интерактивной.

    Практическое правило выбора:

  • Контент, SEO, маркетинговые страницы → чаще SSR/SSG
  • Внутренние кабинеты, сложные интерфейсы «после логина» → иногда SPA или гибрид
  • Большие проекты → часто гибрид, где часть маршрутов SSR, часть SSG, часть SPA‑поведения
  • Из чего состоит Nuxt-приложение

    Nuxt-приложение удобно воспринимать как набор слоёв.

    !Общая схема слоёв Nuxt: UI, маршруты и layout, общая логика, серверные API и внешние источники данных

    UI-слой: страницы, компоненты, layout

  • Страницы (pages) — это верхнеуровневые компоненты, привязанные к маршрутам.
  • Компоненты (components) — переиспользуемые части интерфейса.
  • Layouts (layouts) — «каркас» страниц: общая шапка, подвал, сайдбар и т.д.
  • Слой логики: composables, state, utils

  • Composables (composables) — функции (обычно начинаются с use...), которые инкапсулируют логику и состояние.
  • State management — хранение состояния приложения (в Nuxt часто используют Pinia, но базовые состояния можно хранить и через composables).
  • Если вы используете Pinia, ориентируйтесь на официальную документацию:

  • Документация Pinia
  • Серверный слой: Nitro и server routes

    Nuxt использует Nitro как серверный runtime. Он позволяет:

  • Создавать API‑эндпоинты прямо в проекте
  • Делать серверные маршруты/обработчики
  • Деплоить приложение на разные платформы (Node.js, serverless, edge) в зависимости от адаптера
  • Внутри проекта это обычно выражается папками server/api и server/routes.

    Конвенции важнее конфигурации

    Nuxt следует принципу: меньше ручной настройки, больше договорённостей.

    Самый заметный пример — файловый роутинг.

  • Файл pages/index.vue становится маршрутом /
  • Файл pages/about.vue становится маршрутом /about
  • Динамический маршрут обычно задаётся параметром в имени файла (например, pages/users/[id].vue)
  • Это ускоряет разработку и делает структуру проекта «самодокументируемой».

    Типичная структура проекта

    Структура может отличаться, но базовые папки встречаются чаще всего:

  • pages/ — маршруты приложения
  • components/ — UI‑компоненты
  • layouts/ — макеты страниц
  • composables/ — переиспользуемая логика use...
  • plugins/ — подключение библиотек и глобальных расширений
  • middleware/ — промежуточная логика на маршрутах (например, проверки авторизации)
  • server/ — серверные обработчики (API, routes)
  • assets/ — ресурсы, которые проходят сборку (например, стили)
  • public/ — статические файлы, которые раздаются как есть
  • В некоторых проектах используется папка app/ как «корень» для части структуры (в зависимости от шаблона и настроек). Важно не название папки само по себе, а то, что Nuxt ожидает определённые директории и автоматически подключает их.

    Как проходит запрос в SSR-приложении (упрощённо)

    Ниже — базовый сценарий для SSR‑страницы:

  • Пользователь открывает URL в браузере.
  • Запрос приходит в серверный runtime (Nitro).
  • Nuxt определяет, какая страница соответствует маршруту.
  • Выполняются необходимые загрузки данных (если вы их описали в логике страницы/компонентов).
  • Сервер формирует HTML и отправляет его в браузер.
  • Браузер загружает JS‑бандл.
  • Происходит гидратация: Vue «подключается» к уже готовому HTML.
  • Дальнейшие переходы по страницам часто происходят как в SPA (без полной перезагрузки).
  • Зачем это понимать:

  • Это объясняет, почему часть кода выполняется на сервере, а часть — в браузере.
  • Это помогает избегать ошибок, связанных с доступом к window/document там, где их нет (на сервере).
  • Минимальные примеры: страница и API-эндпоинт

    Пример страницы

    Пример серверного API

    Идея проста:

  • UI живёт в pages/ и components/.
  • Серверные данные/эндпоинты живут в server/.
  • На продакшене это обычно собирается и деплоится как единое приложение.
  • Что важно запомнить перед следующими темами

  • Nuxt — это универсальный фреймворк: клиент + сервер в одном проекте.
  • Архитектура строится вокруг конвенций: pages, layouts, server, plugins.
  • Nitro — ключ к серверным возможностям Nuxt.
  • Рендеринг (SSR/SSG/SPA) влияет на то, где выполняется код и как быстро пользователь видит страницу.
  • В следующих материалах курса мы перейдём к созданию проекта, разбору конфигурации, роутингу, работе с данными и практикам, которые приближают приложение к продакшену.

    2. Маршрутизация, страницы, макеты и навигация

    Маршрутизация, страницы, макеты и навигация

    Как эта тема связана с архитектурой Nuxt

    В предыдущей статье мы разобрали, что Nuxt — это универсальный фреймворк, где клиентский UI и серверный runtime (Nitro) живут в одном проекте, а основной принцип — конвенции важнее конфигурации.

    Маршрутизация в Nuxt — один из самых ярких примеров этого принципа:

  • вы создаёте файлы в pages/, а Nuxt автоматически превращает их в маршруты
  • вы группируете страницы по папкам, а Nuxt автоматически строит вложенные URL
  • вы описываете параметры в имени файла, а Nuxt автоматически создаёт динамические маршруты
  • Официальные материалы для справки:

  • Nuxt pages directory
  • Nuxt layouts directory
  • Nuxt middleware directory
  • Документация Vue Router
  • Файловая маршрутизация: как pages/ превращается в URL

    Nuxt строит маршруты на основе структуры файлов в pages/.

    Базовые правила:

  • pages/index.vue соответствует /
  • pages/about.vue соответствует /about
  • папки внутри pages/ становятся сегментами URL
  • index.vue внутри папки соответствует корню этого сегмента
  • !Соответствие файлов в pages и итоговых маршрутов

    Таблица соответствий

    | Файл в pages/ | Маршрут | |---|---| | pages/index.vue | / | | pages/about.vue | /about | | pages/blog/index.vue | /blog | | pages/blog/post.vue | /blog/post | | pages/users/index.vue | /users |

    Важно: Nuxt использует Vue Router под капотом, но вам обычно не нужно вручную описывать список маршрутов.

    Динамические маршруты: параметры в URL

    Динамический маршрут нужен, когда часть URL меняется, например id пользователя или slug статьи.

    В Nuxt динамика задаётся квадратными скобками в имени файла:

  • pages/users/[id].vue создаёт маршрут /users/:id
  • pages/blog/[slug].vue создаёт маршрут /blog/:slug
  • Как читать параметры на странице

    Внутри страницы параметры доступны через useRoute().

    Практические замечания:

  • route.params содержит параметры как строки (это важно для id, который вы могли ожидать числом)
  • параметры появляются только на маршрутах, где они объявлены (если вы на /users, то id не будет)
  • Catch-all маршруты

    Иногда нужно обработать произвольную глубину пути, например для CMS-подобных страниц: /docs/a, /docs/a/b, /docs/a/b/c.

    Для этого используют catch-all:

  • pages/docs/[...slug].vue соответствует /docs/*
  • Чтение параметра будет выглядеть так:

    Здесь parts обычно является массивом сегментов (например, ['a','b','c']). Это удобно, когда вы строите запрос к API или CMS на основе массива.

    Вложенные маршруты: когда URL и интерфейс имеют иерархию

    Вложенная структура URL обычно отражает структуру интерфейса.

    Примеры, где это встречается:

  • список и карточка сущности: /users и /users/123
  • раздел и подразделы: /settings/profile, /settings/security
  • Вложенность через папки

    Самый частый подход — просто разложить файлы по папкам:

  • pages/settings/index.vue/settings
  • pages/settings/profile.vue/settings/profile
  • pages/settings/security.vue/settings/security
  • Когда нужен “родительский” контейнер для дочерних страниц

    Иногда вы хотите, чтобы у раздела был общий интерфейс, а внутрь подставлялись дочерние страницы (например, общий сайдбар в разделе настроек).

    В Nuxt это делается через механизм вложенных маршрутов, где родительская страница должна отрисовать место для дочерней.

    Ключевые элементы, которые стоит запомнить:

  • NuxtPage — компонент, который отрисовывает текущую страницу (или дочерний маршрут внутри родителя)
  • файловая структура определяет, какие страницы являются дочерними
  • На практике это означает: если вы строите раздел с “рамкой” и внутренними вкладками, вам почти всегда понадобится родительская страница-контейнер.

    Route groups: группировка файлов без влияния на URL

    Иногда хочется сгруппировать страницы в файловой системе (для читаемости), но не менять URL.

    Для этого используются группы маршрутов с круглыми скобками в имени папки.

    Идея:

  • папка (admin) группирует файлы
  • сегмент (admin) не попадает в URL
  • Пример логики:

  • pages/(admin)/users.vue может соответствовать /users
  • Это полезно для больших проектов, где важно поддерживать порядок в pages/.

    Макеты: layouts/ как каркас страниц

    Макет (layout) — это общий каркас для набора страниц: шапка, подвал, боковая панель, общая сетка.

    Чем layout отличается от компонента:

  • layout определяет структуру страницы на уровне маршрута
  • компонент — это переиспользуемый элемент внутри страницы
  • Layout по умолчанию

    Если вы создаёте layouts/default.vue, он будет применяться ко всем страницам, где явно не выбран другой layout.

    Выбор layout для конкретной страницы

    Обычно layout задают через метаданные страницы.

    Здесь:

  • layout: 'auth' означает, что будет использован файл layouts/auth.vue
  • если такого файла нет, Nuxt не сможет применить layout (это ошибка уровня проекта)
  • Когда полезно иметь несколько макетов

    Типичные наборы layout в продакшен-приложении:

  • default для публичной части
  • auth для страниц входа и регистрации
  • dashboard для личного кабинета
  • empty для страниц, где нужен “чистый” экран (например, спец-лендинги)
  • Навигация: переходы между страницами

    В Nuxt есть два важных сценария навигации:

  • переходы по ссылкам внутри приложения без перезагрузки страницы
  • программная навигация из кода после события (например, после логина)
  • Навигация ссылками

    Для внутренних переходов используют NuxtLink.

    Почему это важно:

  • переход происходит клиентски (быстрее, чем полная перезагрузка)
  • Nuxt может подгружать данные и чанки более эффективно
  • сохраняется SPA-ощущение, при этом SSR/SSG продолжают работать
  • Если вам нужно перейти на внешний сайт, используйте обычную ссылку, чтобы поведение было ожидаемым для пользователя.

    Программная навигация

    Есть два популярных инструмента:

  • navigateTo() — nuxt-ориентированный способ перехода
  • useRouter() — доступ к роутеру (подходит, когда нужны более низкоуровневые возможности)
  • Пример с navigateTo():

    Пример через роутер:

    Что важно понимать про query:

  • query добавляет параметры строки запроса
  • пример выше приведёт к URL вида /users?tab=active
  • Получение текущего маршрута

    Чтобы прочитать путь, query или hash, используют useRoute():

    Практическое правило:

  • useRoute() — “читать, где мы сейчас”
  • useRouter() или navigateTo() — “перейти куда-то”
  • Навигационные middleware: контроль доступа и сценарии входа

    Middleware — это код, который выполняется при переходах между страницами. Частый кейс — проверка авторизации.

    В Nuxt middleware обычно размещают в middleware/.

    Типы применения:

  • глобально для многих маршрутов
  • точечно для конкретной страницы
  • Пример: middleware “только для авторизованных”

    Логику обычно строят вокруг того, есть ли у пользователя сессия или токен.

    Здесь:

  • to — маршрут, на который вы пытаетесь перейти
  • from — маршрут, с которого вы уходите
  • navigateTo('/login') делает редирект
  • В реальном проекте isLoggedIn берётся из состояния (например, Pinia) или composable, который читает сессию.

    Подключение middleware на странице

    Часто middleware подключают через метаданные страницы:

    Это означает: при входе на страницу будет выполнен middleware/auth.

    Частые ошибки и как их избежать

  • Обращение к window или document без учёта SSR
  • Ожидание, что route.params.id будет числом
  • Попытка использовать внутреннюю навигацию обычной ссылкой, из-за чего теряются преимущества клиентских переходов
  • Смешивание ответственности: layout для каркаса, компоненты для UI-частей, composables для логики
  • Что важно запомнить

  • pages/ определяет маршруты автоматически
  • квадратные скобки в имени файла создают динамические параметры
  • layouts/ задаёт каркас страниц, а layout выбирается через definePageMeta
  • для внутренней навигации используйте NuxtLink, для переходов из кода — navigateTo или router.push
  • middleware помогает контролировать доступ и выполнять редиректы при навигации
  • 3. Работа с данными: SSR, fetch, useAsyncData и кэширование

    Работа с данными: SSR, fetch, useAsyncData и кэширование

    Зачем в Nuxt отдельно говорить о загрузке данных

    В прошлых темах мы разобрали архитектуру Nuxt и файловую маршрутизацию: страницы живут в pages/, макеты в layouts/, навигация делается через NuxtLink и navigateTo(). Следующий логичный шаг: как наполнять эти страницы данными так, чтобы SSR работал предсказуемо, переходы были быстрыми, а запросы не дублировались.

    В Nuxt приложение может рендериться на сервере (SSR), генерироваться статически (SSG) или вести себя как SPA на клиенте. Это напрямую влияет на то:

  • где выполняется код загрузки данных
  • когда пользователь увидит контент
  • как избежать лишних запросов при гидратации и навигации
  • Официальные ссылки по теме:

  • useAsyncData
  • useFetch
  • fetch
  • Вокруг слова fetch часто возникает путаница.

  • fetch() в браузере и Node.js: это стандартный Web API.
  • fetch и интегрирован в SSR, payload и реактивность.
  • Почему в Nuxt чаще рекомендуют fetch:

    Что здесь важно:

  • ключ 'posts' используется Nuxt для идентификации данных и их кэширования
  • await в script setup позволяет дождаться данных на сервере до рендера HTML (это и даёт полноценный SSR-контент)
  • Как выбирать ключи для useAsyncData

    Ключ должен отражать что именно вы грузите. Если данные зависят от параметров маршрута, ключ должен тоже зависеть от них.

    Пример для динамического маршрута pages/posts/[id].vue:

    vue <script setup lang="ts"> const { data, pending } = await useFetch('/api/recommendations', { server: false }) </script> ts // server/api/weather.get.ts export default cachedEventHandler( async () => { const data = await fetch('/api/posts'))

    async function addPost() { await fetch

  • ключи в useAsyncData критичны: они должны учитывать параметры маршрута
  • кэширование бывает на нескольких уровнях: payload между SSR и клиентом, дедупликация по ключу, и серверный кэш Nitro
  • продакшен-подход часто включает server routes как прокси-слой к внешним API
  • 4. Состояние и логика: composables, Pinia, middleware

    Состояние и логика: composables, Pinia, middleware

    Как эта тема продолжает предыдущие

    В предыдущих статьях мы:

  • разобрали архитектуру Nuxt и разделение на UI-слой, слой логики и серверный слой (Nitro)
  • научились строить маршруты через pages/, подключать layouts/ и управлять навигацией
  • разобрали, как правильно загружать данные для SSR через useAsyncData, useFetch и кэширование
  • Теперь задача следующего уровня: собрать всё это в устойчивую структуру, где:

  • бизнес-логика не размазана по страницам
  • состояние предсказуемо (и не ломается в SSR)
  • доступ к страницам контролируется единым способом через middleware
  • Ключевые инструменты Nuxt для этого:

  • composables — переиспользуемые функции с логикой и состоянием
  • Pinia — полноценный менеджер состояния для приложения
  • middleware — контроль навигации и сценариев доступа на уровне маршрутов
  • Полезные официальные источники:

  • Composables в Nuxt
  • useState
  • Middleware в Nuxt
  • Pinia
  • Модуль Pinia для Nuxt
  • !Как связаны страницы, middleware, состояние и серверные API

    Composables как способ организовать логику

    Что такое composable

    Composable — это функция (обычно useSomething()), которая инкапсулирует логику и может возвращать:

  • состояние (ref, computed, useState)
  • методы (функции действий)
  • интеграцию с Nuxt (например, useAsyncData, useRuntimeConfig, useRoute)
  • В Nuxt composables обычно живут в папке composables/ и автоматически импортируются.

    Пример структуры:

  • composables/useApi.ts
  • composables/useCurrentUser.ts
  • composables/useCurrency.ts
  • Почему composables важны именно в Nuxt (и особенно при SSR)

    Composable помогает сделать логику:

  • переиспользуемой между страницами и компонентами
  • тестируемой (по крайней мере частично)
  • SSR-безопасной, если вы соблюдаете правила
  • Главная SSR-идея:

  • один и тот же код может выполниться на сервере и на клиенте
  • значит, composable не должен безусловно обращаться к window, document, localStorage
  • Если доступ к браузерным API нужен, обычно делают так:

  • условие if (import.meta.client) { ... }
  • или выполнение только в onMounted
  • Правило: не храните глобальное состояние в модульной области

    Неправильный подход:

    Почему это плохо:

  • на SSR один Node-процесс обслуживает много пользователей
  • модульная область может стать общей для разных запросов
  • итог: риск утечки данных между пользователями
  • В Nuxt для общего состояния используйте useState или Pinia.

    useState: простой и SSR-дружелюбный глобальный state

    useState создаёт реактивное состояние, привязанное к Nuxt-приложению и корректно работающее при SSR.

    Что важно:

  • ключ 'counter' должен быть уникальным в пределах приложения
  • функция-инициализатор () => 0 вызывается, когда состояние создаётся впервые
  • Когда useState подходит:

  • небольшие глобальные состояния (флажки, UI-настройки, простая «сессия»)
  • прототипы и небольшие приложения
  • Когда лучше Pinia:

  • много сущностей и сложные сценарии
  • нужны getters, actions, плагины, devtools
  • нужно единое место, похожее на «слой доменной логики»
  • Пример composable для API: единая точка запросов

    Частая продакшен-практика: не вызывать fetch.create({ baseURL: config.public.apiBase }) } ts // nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { public: { apiBase: '' } } }) vue <script setup lang="ts"> const api = useApi()

    const { data, pending, error } = await useAsyncData('posts', () => api('/api/posts')) </script> ts // composables/useCurrentUser.ts export function useCurrentUser() { const api = useApi() const user = useState<any | null>('currentUser', () => null)

    async function fetchMe() { const data = await api('/api/me') user.value = data return data }

    return { user, fetchMe } } bash npm i pinia @pinia/nuxt ts // nuxt.config.ts export default defineNuxtConfig({ modules: ['@pinia/nuxt'] }) ts // stores/auth.ts import { defineStore } from 'pinia'

    type User = { id: string email: string } | null

    export const useAuthStore = defineStore('auth', () => { const token = useCookie<string | null>('token', { sameSite: 'lax' }) const user = ref<User>(null)

    const isLoggedIn = computed(() => Boolean(token.value))

    async function login(email: string, password: string) { const data = await fetch<User>('/api/me', { headers: { Authorization: Bearer ${token.value} } }) }

    function logout() { token.value = null user.value = null }

    return { token, user, isLoggedIn, login, fetchMe, logout } }) vue <script setup lang="ts"> import { storeToRefs } from 'pinia'

    const auth = useAuthStore() const { user, isLoggedIn } = storeToRefs(auth) </script>

    <template> <div> <p v-if="isLoggedIn">Пользователь: {{ user?.email }}</p> <p v-else>Вы не вошли</p> </div> </template> ts // middleware/auth.ts export default defineNuxtRouteMiddleware(async () => { const auth = useAuthStore()

    if (auth.isLoggedIn && !auth.user) { await auth.fetchMe() }

    if (!auth.isLoggedIn) { return navigateTo('/login') } }) vue <script setup lang="ts"> definePageMeta({ middleware: ['auth'] }) </script>

    <template> <main>Секретная страница</main> </template> ts // middleware/guest.ts export default defineNuxtRouteMiddleware(() => { const auth = useAuthStore()

    if (auth.isLoggedIn) { return navigateTo('/dashboard') } }) ts definePageMeta({ middleware: ['guest'] }) ts // middleware/init.global.ts export default defineNuxtRouteMiddleware(async () => { const auth = useAuthStore()

    if (auth.isLoggedIn && !auth.user) { await auth.fetchMe() } }) ts definePageMeta({ middleware: [ () => { const auth = useAuthStore() if (!auth.isLoggedIn) return navigateTo('/login') } ] }) ts // server/middleware/log.ts export default defineEventHandler((event) => { const url = event.node.req.url console.log('[request]', url) }) `

    Когда выбирать что:

    | Задача | Route middleware | Server middleware | |---|---|---| | Редирект на /login при переходе на страницу | Да | Нет | | Проверка роли для маршрута UI | Да | Нет | | Добавить security-headers ко всем ответам | Нет | Да | | Логировать все HTTP-запросы на сервер | Нет | Да |

    Практические продакшен-принципы организации логики

  • Держите «загрузку данных под страницу» в pages/ через useAsyncData, а общую авторизацию и пользователя — в Pinia.
  • Composables используйте как слой «функций» и интеграций: форматирование, API-клиент, повторяемые сценарии.
  • Ключи в useAsyncData выбирайте так, чтобы они зависели от параметров маршрута, иначе появятся неправильные переиспользования данных.
  • В SSR избегайте хранения состояния в модульной области и прямого доступа к браузерным API.
  • Middleware делайте предсказуемыми: минимум сетевых запросов, понятные редиректы, разделение auth и guest.
  • Что важно запомнить

  • Composable — основной способ выносить логику из страниц и компонентов.
  • Для простого глобального состояния в Nuxt есть useState, который корректно работает при SSR.
  • Для крупного приложения лучше использовать Pinia через @pinia/nuxt`.
  • Route middleware управляет доступом и редиректами на уровне маршрутов.
  • Server middleware работает на уровне HTTP-запросов Nitro и решает инфраструктурные задачи, а не навигацию UI.
  • 5. SEO, мета-теги, i18n и управление head

    SEO, мета-теги, i18n и управление head

    Зачем Nuxt-приложению управлять head

    В прошлых статьях мы построили каркас приложения через pages/ и layouts/, научились загружать данные через useAsyncData и организовывать состояние через composables, Pinia и middleware. Следующий шаг, который отличает учебный проект от продакшена, это корректная работа с поисковыми системами и соцсетями:

  • поисковику нужен правильный HTML уже на первом ответе сервера (SSR/SSG)
  • для сниппетов и предпросмотра ссылок нужны мета-теги (title, description, Open Graph)
  • для многоязычности важны корректные lang, переводы и альтернативные версии страниц
  • всё это управляется через head и должно быть детерминированным при SSR
  • Официальные материалы:

  • useHead
  • useSeoMeta
  • Head and SEO
  • Unhead
  • Nuxt i18n
  • Базовая модель SEO в Nuxt

    SEO в контексте Nuxt обычно распадается на несколько уровней.

  • Рендеринг (SSR/SSG)
  • - поисковик получает HTML, где уже есть контент и мета-информация - это особенно важно для контентных страниц и маркетинга
  • Мета-теги и ссылки в head
  • - заголовок страницы (title) - описание (description) - Open Graph и Twitter-карточки для соцсетей - canonical и alternate ссылки для борьбы с дублями
  • i18n и региональные версии
  • - разные языки и URL-стратегии - корректные lang и связность между версиями страниц

    Практический ориентир:

  • если страница должна индексироваться и приносить трафик, то мета-теги должны формироваться на сервере и соответствовать контенту
  • если страница служебная (например, /dashboard), часто важнее безопасность и UX, а индексация может быть отключена
  • Откуда берётся head и как он объединяется

    В Nuxt управление head построено на библиотеке Unhead, которая умеет собирать вклад разных уровней приложения и корректно обновлять head при навигации.

    !Схема того, как Nuxt собирает итоговый head из разных частей приложения

    На практике чаще всего используются три места:

  • глобальные настройки в nuxt.config.ts
  • общие теги в layout (например, базовый шаблон для title)
  • динамика на конкретной странице через useHead или useSeoMeta
  • Глобальные мета-теги в nuxt.config.ts

    Глобальные значения полезны для дефолтов: charset, viewport, базовый шаблон title, общие Open Graph поля для бренда.

    Пример:

    Почему здесь используются функции () => ...:

  • так значения остаются реактивными
  • при клиентских переходах мета-теги обновятся вместе с данными
  • Связь с темой кэширования:

  • если SSR уже загрузил post через useAsyncData, Nuxt передаст результат в payload и избежит лишнего запроса при гидратации
  • Canonical и борьба с дублями URL

    Если одна и та же страница может открываться по разным URL (например, из-за параметров ?utm=...), поисковик может считать это дублями.

    Один из инструментов управления дублями, это canonical.

    Пример задания canonical:

    Практические замечания:

  • base URL лучше хранить в runtimeConfig.public, чтобы переключать окружения
  • canonical должен указывать на основную версию страницы без маркетинговых параметров
  • Управление индексацией: robots и noindex

    Не все страницы стоит индексировать. Примеры:

  • страницы после логина
  • страницы с персональными данными
  • технические страницы
  • На уровне конкретной страницы можно явно поставить robots:

    Важная связь с middleware из предыдущей статьи:

  • middleware защищает доступ
  • noindex защищает от индексации
  • это разные уровни, и один не заменяет другой
  • i18n: что это и какие решения ожидаются в продакшене

    i18n это инфраструктура многоязычности: переводы, форматирование, переключение языка, стратегия URL.

    Что обычно требуется от многоязычного Nuxt-приложения:

  • хранить переводы по языкам
  • иметь стратегию URL (например, /en/about и /ru/about)
  • формировать корректные мета-теги на каждом языке
  • выставлять корректный язык документа (lang)
  • связывать версии страниц через alternate (hreflang)
  • Для Nuxt чаще всего используется модуль @nuxtjs/i18n.

    Документация:

  • Nuxt i18n
  • Подключение Nuxt i18n и базовая конфигурация

    Минимальная конфигурация в nuxt.config.ts зависит от выбранной стратегии и способа хранения переводов.

    Пример концепции с двумя языками и префиксами в URL:

    Что означает strategy: 'prefix_except_default':

  • дефолтный язык остаётся без префикса, например /about
  • остальные языки получают префикс, например /en/about
  • Переводы в компонентах и страницах

    После подключения i18n обычно доступны:

  • функция t или {ruPath}
  • return [ { code: 'ru', href: {ruPath} }, { code: 'en', href: {enPath} } ] })

    useHead({ link: () => alternates.value.map((a) => ({ rel: 'alternate', hreflang: a.code, href: a.href })) }) </script> ``

    Практическое замечание:

  • модуль i18n умеет автоматизировать часть SEO-интеграций, но даже если вы включили автоматизацию, важно понимать что именно добавляется в head и почему
  • Продакшен-правила: где держать SEO-логику

    Чтобы SEO не превратилось в хаос из разбросанных useHead по компонентам, удобно следовать правилам.

  • на уровне nuxt.config.ts задавайте дефолты
  • на уровне layouts/ держите общие вещи, которые относятся ко всем страницам данного каркаса
  • на уровне pages/ задавайте финальный title/description/canonical для конкретного маршрута
  • для контентных страниц формируйте SEO на основе данных, загруженных через useAsyncData
  • избегайте установки SEO-тегов в глубоко вложенных UI-компонентах, если эти теги должны отражать страницу целиком
  • Частые ошибки

  • задавать title только на клиенте (например, в onMounted), из-за чего SSR-ответ приходит без корректного head
  • формировать canonical без учёта окружения и получить ссылки на localhost в продакшене
  • переводить текст страницы, но забывать переводить title и description
  • использовать один и тот же description для всех страниц, теряя релевантность сниппетов
  • не учитывать, что защищённые страницы тоже могут быть случайно проиндексированы без noindex
  • Что важно запомнить

  • корректный head в Nuxt формируется и на сервере, и на клиенте, поэтому SEO-логика должна быть SSR-дружелюбной
  • useHead это универсальный инструмент, useSeoMeta это удобный SEO-слой поверх него
  • динамические мета-теги должны опираться на данные, загруженные через useAsyncData, чтобы SSR отдал правильный HTML
  • i18n влияет на SEO: нужен lang, локализованные мета-теги и связность языковых версий страниц через alternate
  • продакшен-структура обычно держит дефолты в nuxt.config.ts`, а финальные SEO-значения на уровне страниц
  • 6. Оптимизация: производительность, изображения и бандл

    Оптимизация: производительность, изображения и бандл

    Как эта тема продолжает курс

    В предыдущих материалах мы научились строить Nuxt-приложение по конвенциям, настраивать маршрутизацию и макеты, загружать данные SSR-дружелюбными способами, организовывать состояние через composables и Pinia, а также управлять SEO через head.

    Оптимизация в Nuxt 4 — это следующий продакшен-слой, который отвечает на практические вопросы:

  • почему страница ощущается медленной, даже если сервер отвечает быстро
  • как не «убить» скорость сайта изображениями
  • как держать размер клиентского бандла под контролем, чтобы не платить за каждый переход большим JS
  • Оптимизация в Nuxt — это не одна настройка, а набор решений на стыке SSR, данных, маршрутов, ассетов и сборки.

    Полезные источники:

  • Web Vitals
  • Nuxt Image
  • Route Rules
  • NuxtLink
  • Vite: производительность
  • !Куда уходит время при загрузке SSR-страницы

    Ментальная модель производительности в Nuxt

    Производительность удобнее разложить на несколько уровней, потому что разные оптимизации влияют на разные ощущения.

  • Время до первого ответа сервера (часто связывают с TTFB)
  • Время до появления основного контента (часто коррелирует с LCP)
  • Стабильность верстки во время загрузки (часто связывают с CLS)
  • Время до полноценной интерактивности (часто упирается в размер и выполнение JS)
  • Практический вывод для Nuxt:

  • SSR помогает показать контент раньше, но не отменяет стоимость гидратации и выполнения JS
  • изображения и шрифты часто определяют LCP сильнее, чем скорость API
  • большой бандл делает переходы и интерактивность заметно хуже даже при хорошем SSR
  • Где чаще всего теряется скорость в Nuxt-приложении

    Лишние запросы и лишняя работа на сервере

    Типовые причины:

  • страница делает несколько последовательных запросов вместо параллельных
  • серверные API-роуты ходят во внешние сервисы без кэша
  • SSR запускает тяжёлую логику на каждый запрос
  • Что делать в контексте тем курса:

  • грузить данные через useAsyncData или useFetch, чтобы Nuxt корректно передавал результат в payload и не дублировал запросы после гидратации
  • использовать серверный слой server/api как единое место для внешних интеграций и кэшировать там, когда это уместно
  • Перегруженный клиентский JavaScript

    Даже при идеальном SSR пользователь может увидеть контент, но интерфейс будет «тяжёлым»:

  • долго выполняется JS
  • долго происходит гидратация
  • обработчики событий и реактивность подключаются поздно
  • Это почти всегда связано с размером бандла, количеством зависимостей и тем, что вы грузите слишком много кода до того, как он реально нужен.

    Изображения как главный источник веса и LCP

    На большинстве сайтов самый тяжёлый ресурс — это изображения. Если не оптимизировать их системно, вы будете терять скорость независимо от SSR и качества кода.

    Основные проблемы:

  • отдаются оригиналы без ресайза
  • используются устаревшие форматы там, где можно современный
  • нет заданных размеров, из-за чего скачет верстка
  • грузится слишком много изображений сразу, даже если они ниже первого экрана
  • Маршрутные стратегии и кэширование: быстрый ответ сервера

    Nuxt позволяет гибко управлять тем, как обрабатываются разные маршруты. Это напрямую влияет на нагрузку и скорость ответа.

    Route Rules как инструмент продакшен-оптимизации

    routeRules позволяют задавать поведение для групп маршрутов. Обычно их используют, чтобы:

  • включать или отключать SSR для отдельных страниц
  • включать стратегии статической отдачи там, где данные редко меняются
  • управлять кэшированием на уровне маршрутов в зависимости от платформы деплоя
  • Пример идеи конфигурации:

    Что здесь важно понимать:

  • prerender обычно применяют к публичным страницам, которые хорошо подходят под SSG
  • приватные части чаще остаются SSR, потому что зависят от пользователя и сессии
  • Точные возможности зависят от окружения и версии, поэтому ориентируйтесь на документацию: Route Rules

    Кэш на серверном слое

    Если ваш server/api/* проксирует внешний API, кэширование на сервере часто даёт самый быстрый выигрыш:

  • меньше внешних запросов
  • ниже время ответа
  • выше устойчивость под нагрузкой
  • На уровне Nitro есть механизмы кэширования обработчиков. Концепция и ограничения описаны в документации Nitro: Nitro cache

    Бандл: как держать клиентский код под контролем

    Почему размер бандла критичен именно в Nuxt

    Nuxt-приложение почти всегда работает в гибридном режиме:

  • первый рендер может прийти с сервера
  • после этого на клиенте всё равно нужно скачать и выполнить JS для гидратации и переходов
  • То есть вы платите за JS не один раз:

  • при первом входе
  • на новых страницах, когда подгружаются новые чанки
  • Встроенное разделение кода и что ещё можно улучшить

    Nuxt уже делает code-splitting по маршрутам, но часто остаются проблемы:

  • тяжёлая библиотека импортируется в layout и попадает на все страницы
  • большой UI-компонент подключён сразу, хотя нужен только после действия пользователя
  • аналитика и виджеты третьих сторон загружаются слишком рано
  • Практические правила:

  • держите layout лёгким: всё, что не нужно на каждой странице, не должно жить в layout
  • тяжёлые виджеты подгружайте лениво, когда они реально нужны
  • не тяните «универсальные» библиотеки, если можно заменить их небольшой утилитой
  • Динамические импорты для тяжёлых фич

    Если часть функциональности нужна редко, её удобно грузить динамически.

    Пример идеи (упрощённо):

    Здесь оптимизация простая:

  • основной бандл меньше
  • редко используемая фича приезжает отдельным чанком
  • Анализ бандла

    Чтобы оптимизировать бандл, важно сначала увидеть, что именно в него попало.

    Общий подход:

  • включить анализатор сборки через инструменты Nuxt CLI и сборщика
  • найти самые тяжёлые зависимости
  • решить, что можно:
  • - заменить - импортировать точечно - грузить лениво - перенести на сервер (например, часть обработки)

    Отправная точка по командам Nuxt CLI: Nuxt commands

    Изображения: как сделать быстро и без потери качества

    Базовые правила оптимизации изображений

    Эти правила работают почти всегда, независимо от того, используете вы модуль или нет:

  • отдавайте картинку нужного размера, а не оригинал
  • используйте современные форматы, когда это возможно
  • задавайте width и height, чтобы не было скачков верстки
  • делайте ленивую загрузку для контента ниже первого экрана
  • Nuxt Image как стандартный продакшен-подход

    Для системной оптимизации изображений в Nuxt обычно используют модуль Nuxt Image: Nuxt Image

    Что он решает:

  • ресайз и оптимизацию изображений через провайдеров
  • генерацию srcset и responsive-логики
  • удобные компоненты для вывода картинок
  • Минимальная идея подключения:

    Пример использования компонента изображения:

    Что важно в этом примере:

  • width и height помогают браузеру заранее выделить место и уменьшить риск сдвигов
  • format позволяет договариваться о современном формате
  • sizes помогает отдать разные размеры под разные экраны и не качать лишнее
  • Плейсхолдеры и восприятие скорости

    Для больших изображений полезно показывать плейсхолдер, чтобы страница выглядела «живой» быстрее. В Nuxt Image есть подходы с плейсхолдерами и качественной подгрузкой, но конкретные опции зависят от вашей конфигурации и провайдера.

    Практическое правило:

  • плейсхолдер улучшает восприятие
  • но не заменяет правильный размер и формат
  • Изображения из внешних источников

    Если картинки приходят с внешнего домена, продакшен-подход обычно такой:

  • разрешить домены в настройках image-модуля
  • по возможности проксировать/оптимизировать через провайдера, а не отдавать «как есть»
  • Детали зависят от провайдера и инфраструктуры, поэтому ориентируйтесь на документацию: Nuxt Image

    !Почему responsive-изображения дают быстрый выигрыш

    Навигация и предзагрузка: ускоряем переходы внутри приложения

    Nuxt использует клиентские переходы между страницами, и это открывает возможность предзагружать ресурсы.

    Ключевой инструмент для ссылок внутри приложения — NuxtLink: NuxtLink

    Практическая идея:

  • предзагрузка полезна для ссылок, по которым пользователь вероятно перейдёт
  • предзагрузка вредна, если вы предзагружаете слишком много и конкурируете за сеть с критичными ресурсами (изображения первого экрана, CSS)
  • Хорошее правило:

  • оставляйте поведение по умолчанию, пока не появятся измеримые проблемы
  • оптимизируйте предзагрузку точечно на самых посещаемых пользовательских путях
  • Третьи стороны: аналитика, чаты, виджеты

    В реальных проектах заметную часть веса и тормозов дают сторонние скрипты.

    Продакшен-подход:

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

    Практический чеклист оптимизации Nuxt-приложения

  • Проверьте метрики на реальном устройстве и в реальной сети, а не только на мощном ноутбуке.
  • Для страниц с SEO делайте SSR или SSG, а не клиентскую загрузку «после маунта».
  • Кэшируйте ответы в серверном слое, если они дорогие и не меняются каждую секунду.
  • Следите за размером бандла, избегайте тяжёлых зависимостей в layout.
  • Грузите редко используемые фичи динамически.
  • Оптимизируйте изображения системно через Nuxt Image: размер, формат, srcset, размеры в разметке.
  • Будьте осторожны с предзагрузкой и сторонними скриптами: они легко съедают выигрыш от SSR.
  • Что важно запомнить

  • Производительность — это сумма: серверный ответ, изображения, JS и гидратация.
  • Nuxt даёт инструменты для стратегии маршрутов и кэширования, но их нужно применять осознанно.
  • Оптимизация бандла — это дисциплина: меньше кода в layout, ленивые фичи, анализ состава зависимостей.
  • Оптимизация изображений часто даёт самый большой и быстрый выигрыш, особенно для LCP и общего веса страницы.
  • 7. Тестирование, CI/CD, деплой и эксплуатация в продакшене

    Тестирование, CI/CD, деплой и эксплуатация в продакшене

    Как эта тема завершает курс

    В предыдущих статьях мы собрали Nuxt-приложение как продакшен-систему: архитектура и конвенции, маршруты и layout, SSR-загрузка данных, состояние через composables и Pinia, SEO и оптимизация производительности.

    Эта статья закрывает последний «инженерный слой»:

  • как тестировать Nuxt-приложение на разных уровнях
  • как собрать CI/CD пайплайн, который не ломает продакшен
  • как деплоить Nuxt (SSR, SSG, serverless/edge) и что важно при выборе
  • как эксплуатировать приложение после релиза: логирование, мониторинг, ошибки, конфигурации
  • Полезные официальные ссылки:

  • Тестирование в Nuxt
  • Nuxt deployment
  • Nitro deployment
  • Документация Vitest
  • Документация Playwright
  • Документация GitHub Actions
  • !Блок-схема типового CI/CD пайплайна для Nuxt

    Тестирование Nuxt-приложений: что именно мы проверяем

    В Nuxt важно тестировать не только Vue-компоненты, но и универсальную часть (SSR), маршрутизацию, серверные эндпоинты Nitro и критические пользовательские сценарии.

    Уровни тестирования

    | Уровень | Что проверяем | Инструменты | Что даёт в продакшене | |---|---|---|---| | Unit | функции, composables, небольшие модули | Vitest | быстрая обратная связь, регрессии логики | | Component | рендер и поведение компонентов | Vue Test Utils + Vitest | стабильность UI-логики без браузера | | Integration | связка «страница + данные + серверные API» | Nuxt test utils | ловит проблемы SSR/рантайма | | E2E | сценарии в браузере (логин, фильтры, критические флоу) | Playwright | защита от «сломали прод» на уровне пользователя |

    Практический ориентир:

  • unit и component тесты должны быть быстрыми и выполняться на каждом PR
  • E2E тесты покрывают только критические пути, иначе они станут дорогими и нестабильными
  • Unit-тесты: composables и бизнес-логика

    Что тестировать в первую очередь

  • composables, которые инкапсулируют логику (валидация, форматирование, преобразование данных)
  • Pinia actions и getters, если они содержат нетривиальную логику
  • утилиты, которые используются в нескольких местах
  • Важное SSR-правило

    Если composable зависит от окружения, тестируйте это явно:

  • код, который должен работать на сервере, не должен обращаться к window и document
  • браузерно-зависимый код должен быть защищён проверками вроде import.meta.client
  • Минимальная структура

    Пример unit-теста на Vitest:

    Здесь мы тестируем чистую логику без Nuxt-контекста. Это самый дешёвый и полезный тип тестов.

    Component-тесты: проверяем UI без браузера

    Component-тесты полезны, когда:

  • у компонента есть ветвления по props
  • есть события (emit) и простая интерактивность
  • важно поведение в изоляции (без маршрутизации и настоящего API)
  • Минимальная идея:

    Важно: компонент-тесты не заменяют E2E. Они проверяют логику компонента, но не проверяют реальный браузерный стек.

    Интеграционные тесты: Nuxt + Nitro вместе

    Интеграционные тесты нужны, чтобы поймать ошибки на границе слоёв:

  • SSR-рендеринг и гидратация
  • загрузка данных через useAsyncData и корректные ключи
  • серверные эндпоинты server/api и поведение кэша/заголовков
  • В Nuxt есть официальные утилиты для тестирования приложения и эндпоинтов: Тестирование в Nuxt.

    Практический подход к интеграционным тестам:

  • тестировать API-роуты как обычные HTTP-обработчики
  • тестировать, что страница отдаёт ожидаемый контент при SSR для SEO-критичных маршрутов
  • E2E-тесты: проверяем приложение глазами пользователя

    E2E (end-to-end) тесты запускают реальный браузер и проходят пользовательский сценарий.

    Что стоит покрыть E2E:

  • логин и логаут
  • доступ к защищённым маршрутам (middleware auth)
  • критические сценарии покупки/создания сущности/заявки
  • базовые проверки «страница открылась, контент есть» для ключевых страниц
  • Инструмент по умолчанию в индустрии: Playwright.

    Минимальная идея сценария:

    Практика для стабильности E2E:

  • используйте стабильные селекторы (например, role/label), а не классы
  • делайте тестовую среду с предсказуемыми данными
  • минимизируйте количество E2E в PR, оставляйте расширенный прогон на main/ночные сборки
  • CI/CD: как превратить тесты и сборку в «ворота» в продакшен

    CI/CD в Nuxt обычно решает две цели:

  • не пропустить регрессии (качество)
  • выкатывать изменения быстро и повторяемо (скорость)
  • Минимальный пайплайн для PR

  • установка зависимостей
  • линтинг
  • проверка типов
  • unit/component тесты
  • сборка (как минимум build, чтобы поймать ошибки сборки)
  • Пайплайн для main

  • всё из PR
  • E2E тесты
  • деплой на staging или сразу в production (в зависимости от процесса)
  • smoke-проверки после деплоя
  • Пример GitHub Actions workflow

    Это базовый «скелет». Дальше вы добавляете E2E и деплой.

    Что хранить в secrets и env

    В CI/CD нельзя хранить секреты в репозитории.

  • секреты (токены, ключи) должны быть в GitHub Secrets или аналогах
  • публичные переменные (например, NUXT_PUBLIC_*) можно задавать в окружении, но всё равно лучше централизованно
  • Важно помнить разделение runtimeConfig:

  • runtimeConfig.public доступен на клиенте, значит не храните там секреты
  • приватные значения держите в приватной части runtimeConfig
  • Официально: Runtime config в Nuxt.

    Деплой Nuxt: SSR, SSG, serverless и edge

    Nuxt 4 собирается через Nitro и поддерживает разные «пресеты деплоя»: Nitro deployment.

    Основные модели деплоя

    | Модель | Когда подходит | Сильные стороны | Риски | |---|---|---|---| | SSR Node server | сложные SSR страницы, сессии, контроль инфраструктуры | предсказуемость, гибкость | нужно обслуживать сервер | | Serverless | нерегулярная нагрузка, быстрый старт | масштабирование «из коробки» | холодные старты, ограничения окружения | | Edge | глобальная аудитория, нужен минимум задержек | близко к пользователю | ограничения рантайма, сложнее отладка | | SSG (generate/prerender) | контент редко меняется | очень быстрые ответы, дешёвый хостинг | обновления через билд/инвалидацию |

    Точка входа в официальную документацию: Nuxt deployment.

    Практический выбор стратегии

  • публичные SEO-страницы часто выигрывают от SSG или SSR с кэшем
  • личные кабинеты и персональные данные чаще требуют SSR и правильной работы с cookie
  • гибридный подход через routeRules (из прошлой статьи) обычно даёт лучший баланс
  • Release-практики: как снижать риск выката

    Staging окружение

    Staging должен быть максимально похож на production:

  • те же переменные окружения по структуре
  • та же конфигурация SSR/кэша
  • реальные сценарии деплоя (те же команды и шаги)
  • Smoke-тесты после деплоя

    Smoke-тесты — короткая проверка «сайт жив»:

  • главная страница отвечает
  • ключевые API отвечают
  • нет 500 на критических маршрутах
  • Это может быть:

  • маленький набор Playwright тестов
  • простая проверка HTTP статусов
  • Rollback

    Простой и работающий подход:

  • хранить предыдущий артефакт сборки
  • уметь быстро переключить версию (или откатить release на платформе)
  • Если у вас есть миграции базы данных, rollback нужно планировать отдельно, потому что «код назад» не всегда совместим со «схемой вперёд».

    Эксплуатация: ошибки, логирование, мониторинг

    Разница между «ошибкой в UI» и «ошибкой на сервере»

    В Nuxt ошибка может случиться:

  • в браузере (гидратация, события, компоненты)
  • на сервере Nitro (SSR, server routes)
  • Продакшен-цель: видеть и понимать оба типа.

    Логирование на сервере

    Серверный слой server/api и server/middleware — хорошее место для логов запросов и ошибок.

    Практика:

  • логируйте контекст (route, статус, время)
  • не логируйте персональные данные
  • добавляйте корреляционный идентификатор запроса, если есть распределённые сервисы
  • Monitoring и error tracking

    Для фронтенд-ошибок и SSR-ошибок часто используют error tracking. Один из стандартов индустрии: Sentry.

    Что важно настроить:

  • source maps для читаемых stack trace
  • фильтрацию шумных ошибок
  • алерты только на действительно важное
  • Health checks

    Для продакшена полезно иметь endpoint вроде /api/health, который проверяет минимум:

  • процесс жив
  • критическая зависимость доступна (например, база или внешний API), если это применимо
  • Это помогает балансировщикам и мониторингу.

    Безопасность и конфигурация продакшена

    Cookies и SSR

    Для авторизации в SSR-режиме чаще выбирают cookie, потому что они доступны на сервере в контексте запроса.

    Практические настройки cookie:

  • httpOnly для сессионных токенов, чтобы их не читал JS
  • sameSite для снижения риска CSRF
  • secure в production (HTTPS)
  • Официально: useCookie в Nuxt.

    Заголовки безопасности

    Часть заголовков удобно ставить на уровне CDN/ингресса, а часть можно добавить через server/middleware.

    Пример server middleware (упрощённо):

    Для строгих политик вроде CSP важно тестировать совместимость со сторонними скриптами.

    Чеклист «готово к продакшену» для Nuxt 4

  • тесты: unit покрывают ключевую логику, E2E покрывают критические сценарии
  • CI: lint, typecheck, tests, build выполняются на PR
  • CD: деплой повторяемый, секреты в безопасном хранилище, есть staging
  • деплой-модель выбрана осознанно (SSR/SSG/serverless/edge) и проверена нагрузкой
  • есть мониторинг ошибок и базовые health checks
  • runtimeConfig разделён на public и private, секреты не утекают на клиент
  • Что важно запомнить

  • В Nuxt тестируют и Vue-часть, и Nitro-часть, и пользовательские сценарии.
  • CI должен ловить ошибки до мержа, а CD должен выкатывать повторяемо и с возможностью отката.
  • Деплой Nuxt — это выбор рантайма Nitro и стратегии рендеринга, а не только «куда залить».
  • Продакшен начинается после релиза: логирование, мониторинг, health checks и безопасная конфигурация важны так же, как код.