Скорость

Оптимизация производительности Nuxt 4: руководство для production

Узнайте, как оптимизировать Nuxt 4 для production с помощью hybrid rendering, prerendering, lazy hydration, уменьшения payload, оптимизации изображений, кэширования и измерения Core Web Vitals.

SeoNest Team2 мин чтения
Открыть содержание статьи

Оптимизация производительности Nuxt 4: руководство для production

Приложение на Nuxt 4 может использовать серверный рендеринг и при этом оставаться медленным. SSR решает лишь часть проблем производительности: браузер всё ещё может получать слишком много JavaScript, гидратировать компоненты, которые пользователю не нужны, загружать слишком большие изображения, ждать медленные API или обрабатывать чрезмерно крупные Nuxt payload.

В production наибольший эффект обычно дают не точечные микрооптимизации, а правильная архитектура: подходящая стратегия рендеринга для каждого маршрута, минимум работы на клиенте, небольшие payload с данными, оптимизация критических ресурсов, кэширование там, где оно возможно, и измерение результата как в лабораторных условиях, так и на реальных пользователях.

Это руководство соответствует актуальной документации Nuxt 4, в которой на момент написания указана версия Nuxt 4.5.2.

Краткий ответ

Чтобы оптимизировать Nuxt 4 для production, начните с пяти направлений:

  1. Выбирайте подходящий рендеринг для каждого маршрута — пререндерите статический контент и используйте серверный рендеринг или кэширование для действительно динамических страниц.
  2. Отправляйте меньше JavaScript — загружайте необязательные компоненты лениво и откладывайте гидратацию там, где интерактивность не требуется сразу.
  3. Сокращайте объём данных — избегайте повторных запросов и сериализуйте только те данные, которые действительно нужны клиенту.
  4. Оптимизируйте ресурсы, влияющие на LCP — особенно hero-изображения, шрифты, CSS и время ответа сервера.
  5. Измеряйте поведение в production — используйте Core Web Vitals, полевые данные и анализ bundle вместо оптимизации на основе предположений.

Nuxt уже предоставляет code splitting, SSR, утилиты для загрузки данных и Nitro. Задача разработчика — настроить эти возможности под реальное поведение приложения. (nuxt.com)

Целевые показатели производительности

Текущие пороговые значения Google для Core Web Vitals:

МетрикаХорошее значениеЧто измеряет
LCP≤ 2.5 sСкорость загрузки
INP≤ 200 msОтзывчивость взаимодействия
CLS≤ 0.1Визуальная стабильность

Эти показатели следует оценивать на 75-м перцентиле, отдельно для мобильного и десктопного трафика. (web.dev)

Это полезные ориентиры, но они не заменяют диагностику. Плохой LCP может быть связан с сервером, поздним обнаружением изображения, CSS, конфигурацией CDN или сразу несколькими задержками.

INTERNAL LINK: Объяснение Core Web Vitals

Выбирайте рендеринг для каждого маршрута

Одно из важнейших решений по производительности Nuxt принимается ещё до оптимизации bundle: нужно ли действительно рендерить эту страницу динамически при каждом запросе?

По умолчанию Nuxt поддерживает серверный рендеринг, а также статический prerendering и гибридное поведение через route rules Nitro. (nuxt.com)

Например:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/about': { prerender: true },
    '/blog/**': { prerender: true },
    '/api/catalog': {
      cache: { maxAge: 60 * 10 }
    }
  }
})

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

С динамическими страницами аккаунта ситуация другая. Они могут зависеть от аутентификации, прав доступа или часто меняющихся персонализированных данных, поэтому обычно должны оставаться динамическими.

Ошибка — выбирать один режим рендеринга для всего приложения только потому, что так проще настроить проект.

Статические страницы можно оптимизировать ещё сильнее

Nuxt 4 также поддерживает noScripts для страниц, которым действительно не нужна клиентская интерактивность. В сочетании с prerendering такой маршрут может отдаваться как HTML и CSS без обычных клиентских скриптов Nuxt и затрат на гидратацию. (nuxt.com)

export default defineNuxtConfig({
  routeRules: {
    '/blog/**': {
      prerender: true,
      noScripts: true
    }
  }
})

Не применяйте это к интерактивным страницам. Маршрут с noScripts не имеет Vue hydration, клиентских обработчиков событий и обычной клиентской навигации Nuxt.

Сокращайте работу гидратации

Серверный рендеринг создаёт HTML, но интерактивной странице Nuxt всё равно нужен Vue, чтобы гидратировать соответствующие компоненты в браузере.

На это тратится процессорное время.

Длинная landing page может содержать:

  • навигацию,
  • hero-контент,
  • отзывы,
  • калькуляторы стоимости,
  • карусели,
  • формы подписки,
  • карты,
  • интеграции аналитики,
  • чат-виджеты.

Не всем этим элементам нужно становиться интерактивными одновременно.

Nuxt поддерживает стратегии отложенной гидратации для lazy-компонентов. Компонент можно гидратировать, когда он становится видимым, когда браузер простаивает, после взаимодействия пользователя или при выполнении другого заданного условия. (nuxt.com)

<template>
  <HeroSection />

  <LazyTestimonials hydrate-on-visible />

  <LazyNewsletterForm hydrate-on-visible />

  <LazyCookieSettings hydrate-on-idle />
</template>

Это особенно полезно для функциональности ниже первого экрана. Не откладывайте гидратацию критически важных элементов первого экрана только ради улучшения performance score; сама документация Nuxt предупреждает, что не следует задерживать гидратацию контента, который нужен пользователю сразу. (nuxt.com)

Lazy loading и lazy hydration также решают разные задачи. Компонент Lazy помогает разделять код, а отложенная гидратация определяет, когда уже отрендеренный на сервере контент станет интерактивным.

INTERNAL LINK: Объяснение гидратации в Nuxt

Уменьшайте payload с данными

useAsyncData и useFetch в Nuxt учитывают SSR. Когда данные загружаются во время серверного рендеринга, Nuxt может передать полученный результат через свой payload, чтобы браузеру не приходилось повторять тот же запрос во время гидратации. (nuxt.com)

Это полезно, но возникает другой вопрос производительности:

Какой объём данных вы сериализуете в страницу?

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

Вместо передачи всего объекта:

const route = useRoute()

const { data: product } = await useAsyncData(
  `product-${route.params.slug}`,
  () => $fetch(`/api/products/${route.params.slug}`),
  {
    pick: ['id', 'name', 'price', 'image'],
    deep: false
  }
)

pick позволяет ограничить результат только необходимыми свойствами. В Nuxt 4 useAsyncData также по умолчанию использует shallow reactivity, что позволяет избежать затрат на глубокую реактивность каждого вложенного свойства, когда она не нужна. (nuxt.com)

Для динамических маршрутов ключи кэша или async-data должны однозначно соответствовать конкретному ресурсу. Повторное использование неоднозначного ключа на разных страницах может привести к некорректному совместному использованию данных.

Nuxt также поддерживает извлечение payload для пререндеренных и кэшированных маршрутов. В зависимости от конфигурации данные payload могут находиться в файлах _payload.json и повторно использоваться при клиентской навигации. (nuxt.com)

Оптимизируйте ресурс LCP

На многих production-сайтах элементом LCP становится большое hero-изображение или крупный текстовый блок.

Google определяет хороший LCP как 2.5 секунды или меньше на 75-м перцентиле, но проблему LCP следует раскладывать на составляющие, а не рассматривать как одно число. (web.dev)

Для hero-изображения проверьте цепочку:

Ответ сервера → обнаружение в HTML → запрос изображения → загрузка → отрисовка

Даже хорошо оптимизированное изображение может ухудшать LCP, если браузер обнаруживает его слишком поздно.

При использовании Nuxt Image задавайте подходящие размеры и responsive sizing, а также рассмотрите предварительную загрузку изображения, которое действительно является LCP-элементом:

<NuxtImg
  src="/images/hero.webp"
  width="1440"
  height="810"
  sizes="sm:100vw lg:1200px"
  :preload="{ fetchPriority: 'high' }"
  alt="Product dashboard"
/>

Nuxt Image поддерживает предварительную загрузку изображений и управление fetch priority. (image.nuxt.com) Рекомендации по web performance также советуют использовать fetchpriority="high" там, где это уместно для важного LCP-изображения, и одновременно предупреждают, что не следует без необходимости повышать приоритет ресурсов. (web.dev)

Не используйте lazy loading для изображения, которое должно стать LCP-элементом.

Контролируйте стоимость JavaScript

Nuxt автоматически выполняет route-level code splitting, однако сторонние зависимости всё равно могут делать отдельные chunks слишком тяжёлыми. (nuxt.com)

Частые причины:

  • редакторы,
  • библиотеки для графиков,
  • SDK карт,
  • библиотеки анимаций,
  • пакеты аналитики,
  • виджеты поддержки клиентов,
  • крупные utility-библиотеки.

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

<script setup lang="ts">
const open = ref(false)
</script>

<template>
  <button @click="open = true">
    Open analytics
  </button>

  <LazyAnalyticsDashboard v-if="open" />
</template>

Соглашение Nuxt для компонентов Lazy использует динамические импорты, поэтому код необязательного компонента можно загрузить позже. (nuxt.com)

Плагины требуют такого же внимания. Nuxt отмечает, что тяжёлая инициализация плагинов может блокировать гидратацию, а асинхронные плагины, которые не зависят друг от друга, можно выполнять параллельно. (nuxt.com)

Исправляйте ошибки гидратации

Предупреждение о hydration mismatch — не просто косметическая проблема.

Nuxt документирует, что несовпадения могут заставить Vue повторно рендерить деревья компонентов, увеличить время до интерактивности, вызвать визуальные сдвиги и нарушить работу обработчиков событий. (nuxt.com)

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

  • сервер и клиент генерируют разные значения,
  • browser-only API используются во время SSR,
  • случайные значения генерируются отдельно,
  • вывод зависит от времени,
  • некорректный client-only rendering.

Исправляйте саму причину расхождения состояния, а не просто скрывайте предупреждение.

Сначала измеряйте

Работа над производительностью должна идти по схеме:

Симптом → измерение → причина → исправление → повторное измерение

Используйте PageSpeed Insights или CrUX для оценки Core Web Vitals на реальных пользователях, если доступен достаточный объём полевых данных. CrUX отражает реальные впечатления пользователей Chrome, а Lighthouse предоставляет контролируемую лабораторную диагностику. (developer.chrome.com)

Для анализа JavaScript проверьте production bundle:

npx nuxt analyze

Команда analyze в Nuxt собирает production-приложение и создаёт анализ bundle, который помогает обнаружить неожиданно крупные зависимости и chunks. Сейчас эта команда в документации отмечена как experimental. (nuxt.com)

Практичный workflow для production:

  1. Проверьте полевые значения LCP, INP и CLS.
  2. Воспроизведите медленный маршрут в реалистичных мобильных условиях.
  3. Изучите network waterfall и performance trace.
  4. Определите фактический блокирующий ресурс или задачу main thread.
  5. Исправьте один существенный bottleneck.
  6. Выполните деплой и снова сравните полевую производительность.

Не оптимизируйте сайт исключительно ради оценки Lighthouse.

Распространённые ошибки

Отключать SSR ради более простой архитектуры.ssr: false переводит приложение на client-side rendering и убирает многие преимущества SSR. Nuxt прямо отмечает, что статический SPA-output содержит изначально пустой контейнер приложения, тогда как SSR prerendering сразу предоставляет HTML страницы. (nuxt.com)

Сразу гидратировать всё. Server-rendered HTML не означает, что работа браузера ничего не стоит. Откладывайте некритичную интерактивность.

Передавать целые объекты API. Сокращение числа запросов при передаче огромных сериализованных payload лишь переносит bottleneck в другое место.

Использовать lazy loading для LCP-изображения. Критически важный контент должен обнаруживаться браузером как можно раньше, а не намеренно задерживаться.

Добавлять каждый экспериментальный performance-механизм. Experimental-функции могут меняться и иметь важные компромиссы. Например, Nuxt 4.5 документирует experimental SSR streaming, но он влияет на момент, когда можно изменять response headers и status, а также автоматически отключается для нескольких конфигураций route rules. Перед глобальным внедрением таких возможностей проверяйте их на реальном приложении. (nuxt.com)

Рекомендация SeoNest

Оптимизируйте Nuxt снаружи внутрь.

Сначала измерьте то, что действительно испытывают пользователи. Затем определите, связан ли bottleneck со временем ответа сервера, стратегией рендеринга, загрузкой критических ресурсов, гидратацией, выполнением JavaScript или передачей данных.

Для большинства production-сайтов на Nuxt приоритет должен быть таким:

правильная стратегия рендеринга → быстрый критический контент → минимальная гидратация → небольшие payload → контролируемый JavaScript → кэширование → постоянные полевые измерения

Технически сложная конфигурация не означает автоматически высокую скорость. Лучшая архитектура Nuxt — та, которая не заставляет сервер, сеть и браузер выполнять лишнюю работу.

FAQ

SSR всегда быстрее?

Нет. SSR может ускорить первоначальную доставку HTML, но медленный серверный рендеринг, крупные payload и тяжёлая гидратация всё равно могут приводить к плохой производительности. Правильный подход зависит от конкретного маршрута.

Нужно ли пререндерить каждую страницу?

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

Улучшает ли lazy loading INP?

Может улучшить, если убирает ненужный JavaScript с критического пути, однако INP зависит от фактической работы main thread вокруг пользовательских взаимодействий. Google рекомендует минимизировать работу обработчиков событий и при необходимости разбивать длинные задачи. (web.dev)

Стоит ли использовать hydrate-never?

Только для контента, которому не нужна клиентская интерактивность. Nuxt может отрендерить такие компоненты на сервере без обычных затрат на их гидратацию. (nuxt.com)

Как понять, что оптимизировать в первую очередь?

Начните с Core Web Vitals реальных пользователей, если такие данные доступны. Затем используйте лабораторные инструменты и browser traces, чтобы найти причину показателя, а не строить догадки только на основе самого значения метрики.

Итог

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

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

Такой подход масштабируется лучше, чем набор отдельных приёмов оптимизации, потому что он устраняет реальные затраты страницы.

Sources

SEONEST

Нужна более сильная техническая основа?

Мы создаём production-ready сайты, где SEO, скорость и чистая разработка заложены в архитектуру с самого начала.

Обсудить проект