Экспериментальные возможности
В Nuxt часть возможностей помечена как экспериментальные; их можно включить в конфигурации.
Описание этих возможностей задаётся в @nuxt/schema. Полный список и параметры — в API-документации и исходном коде.
alwaysRunFetchOnKeyChange
Запускать ли useFetch при изменении ключа, даже если задано immediate: false и запрос ещё не выполнялся.
useFetch и useAsyncData всегда перезапускаются при смене ключа, если immediate: true или запрос уже был выполнен.
По умолчанию флаг выключен; при необходимости включите:
export default defineNuxtConfig({
experimental: {
alwaysRunFetchOnKeyChange: true,
},
})
appManifest
Использовать app manifest, чтобы на клиенте учитывались route rules.
По умолчанию включено; при необходимости отключите:
export default defineNuxtConfig({
experimental: {
appManifest: false,
},
})
asyncContext
Включить нативный async context для вложенных композаблов в Nuxt и Nitro — возможность вызывать композаблы внутри асинхронных и снизить ошибки «Nuxt instance is unavailable».
export default defineNuxtConfig({
experimental: {
asyncContext: true,
},
})
asyncEntry
Включает генерацию асинхронной точки входа для Vue-бандла, что помогает при использовании module federation.
export default defineNuxtConfig({
experimental: {
asyncEntry: true,
},
})
extractAsyncDataHandlers
Выносит функции-обработчики из вызовов useAsyncData и useLazyAsyncData в отдельные чанки для улучшения code splitting и кэширования.
export default defineNuxtConfig({
experimental: {
extractAsyncDataHandlers: true,
},
})
Обработчики, заданные inline, превращаются в динамически подгружаемые чанки:
<!-- До -->
<script setup>
const { data } = await useAsyncData('user', async () => {
return await $fetch('/api/user')
})
</script>
<!-- После преобразования -->
<script setup>
const { data } = await useAsyncData('user', () =>
import('/generated-chunk.js').then(r => r.default()),
)
</script>
Так логика загрузки данных выносится в отдельные чанки, но при необходимости код по-прежнему подгружается.
emitRouteChunkError
Вызывает хук app:chunkError при ошибке загрузки чанков vite/webpack. По умолчанию при переходе на новый маршрут при сбое загрузки чанка выполняется перезагрузка нового маршрута.
По умолчанию Nuxt перезагружает новый маршрут при сбое загрузки чанка при навигации (automatic).
Значение automatic-immediate заставляет Nuxt перезагружать текущий маршрут сразу при сбое загрузки чанка (не дожидаясь навигации). Это полезно для ошибок чанков, не связанных с навигацией, например когда не удаётся загрузить ленивый компонент. Минус — возможные лишние перезагрузки, если приложению не нужен чанк, из-за которого произошла ошибка.
Автоматическую обработку можно отключить, задав false, или обрабатывать ошибки чанков вручную, задав manual.
export default defineNuxtConfig({
experimental: {
emitRouteChunkError: 'automatic', // или 'automatic-immediate', 'manual' или false
},
})
enforceModuleCompatibility
Нужно ли выбрасывать ошибку (и не загружать приложение), если модуль Nuxt несовместим.
По умолчанию отключено.
export default defineNuxtConfig({
experimental: {
enforceModuleCompatibility: true,
},
})
restoreState
Позволяет восстанавливать состояние приложения Nuxt из sessionStorage при перезагрузке страницы после ошибки чанка или вызова reloadNuxtApp().
Чтобы избежать ошибок гидрации, состояние применяется только после монтирования Vue-приложения, из-за чего при первой загрузке возможна краткая «вспышка».
useState, так как автогенерируемые ключи могут не совпадать между сборками.export default defineNuxtConfig({
experimental: {
restoreState: true,
},
})
inlineRouteRules
Задаёт правила маршрутов на уровне страницы через defineRouteRules.
export default defineNuxtConfig({
experimental: {
inlineRouteRules: true,
},
})
Правила создаются по path страницы.
noVueServer
Отключает эндпоинт Vue server renderer в Nitro.
export default defineNuxtConfig({
experimental: {
noVueServer: true,
},
})
parseErrorData
Нужно ли разбирать error.data при рендере страницы серверной ошибки.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
parseErrorData: false,
},
})
payloadExtraction
Управляет тем, как передаются данные payload для пререндеренных и кэшируемых (ISR/SWR) страниц.
'client'— payload встраивается в HTML при первом серверном рендере и выносится в файлы_payload.jsonдля клиентской навигации. Нет отдельного сетевого запроса при первой загрузке и сохраняется эффективная клиентская навигация.true— payload выносится в отдельный_payload.jsonи для первого рендера, и для клиентской навигации.false— извлечение payload отключено. Payload всегда в HTML, файлы_payload.jsonне создаются.
По умолчанию true, при compatibilityVersion: 5 — 'client'.
export default defineNuxtConfig({
experimental: {
// Payload в HTML, в файлы — только для клиентской навигации
payloadExtraction: 'client',
},
})
Извлечение payload работает и для маршрутов с кэшированием ISR (Incremental Static Regeneration) или SWR (Stale-While-Revalidate). CDN могут кэшировать файлы payload вместе с HTML и ускорять клиентскую навигацию по кэшированным маршрутам.
export default defineNuxtConfig({
experimental: {
payloadExtraction: 'client',
},
routeRules: {
// Для этих маршрутов будут создаваться файлы payload
'/products/**': { isr: 3600 },
'/blog/**': { swr: true },
},
})
clientNodePlaceholder
Uses comment nodes (<!--placeholder-->) instead of <div> elements as placeholders for client-only components during server-side rendering.
When enabled, .client.vue components and createClientOnly() wrappers render an HTML comment on the server instead of an empty <div>. This fixes a Vue hydration issue where scoped styles may not be applied when the placeholder <div> and the actual component root share the same tag name.
class, style, etc.) passed to .client.vue components will not appear in the SSR HTML. If you need styled placeholders to prevent layout shift, use <ClientOnly> with a #fallback slot instead.This flag is enabled when future.compatibilityVersion is set to 5 or higher, but you can also enable it explicitly:
export default defineNuxtConfig({
experimental: {
clientNodePlaceholder: true,
},
})
clientFallback
Включает экспериментальный компонент <NuxtClientFallback> для рендера контента на клиенте при ошибке SSR.
export default defineNuxtConfig({
experimental: {
clientFallback: true,
},
})
crossOriginPrefetch
Включает cross-origin prefetch через Speculation Rules API.
export default defineNuxtConfig({
experimental: {
crossOriginPrefetch: true,
},
})
viewTransition
Включает интеграцию View Transition API с клиентским роутером.
export default defineNuxtConfig({
experimental: {
viewTransition: true,
},
})
Можно передать объект для настройки типов view transition — разных CSS-анимаций в зависимости от типа навигации:
export default defineNuxtConfig({
experimental: {
viewTransition: {
enabled: true,
types: ['slide'],
},
},
})
writeEarlyHints
Включает отправку early hints при использовании node-сервера.
export default defineNuxtConfig({
experimental: {
writeEarlyHints: true,
},
})
componentIslands
Включает экспериментальную поддержку component islands с <NuxtIsland> и файлами .island.vue.
export default defineNuxtConfig({
experimental: {
componentIslands: true, // false или 'local+remote'
},
})
localLayerAliases
Разрешать алиасы ~, ~~, @ и @@ внутри слоёв относительно исходной и корневой директорий слоя.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
localLayerAliases: false,
},
})
typedPages
Включает новый экспериментальный типизированный роутер.
export default defineNuxtConfig({
experimental: {
typedPages: true,
},
})
По умолчанию даёт типизированное использование navigateTo, <NuxtLink>, router.push() и др.
Типизированные параметры в странице: const route = useRoute('route-name').
watcher
Задаёт альтернативный watcher для отслеживания файлов в Nuxt.
По умолчанию используется chokidar-granular, который игнорирует корневые каталоги (например node_modules и .git).
Значение parcel подключает @parcel/watcher и может улучшить производительность в больших проектах и на Windows.
Значение chokidar включает отслеживание всех файлов в исходной директории.
Значение 'builder' переиспользует собственный file watcher активного сборщика (например, server.watcher у Vite) вместо запуска второго. Это уменьшает число watcher'ов в режиме разработки и станет значением по умолчанию при future.compatibilityVersion: 5. Если у сборщика нет своего watcher'а (сейчас webpack и rspack), Nuxt выводит предупреждение и возвращается к выбору по умолчанию.
export default defineNuxtConfig({
experimental: {
watcher: 'chokidar-granular', // также возможны 'chokidar', 'parcel' или 'builder'
},
})
sharedPrerenderData
Nuxt автоматически делится данными payload между пререндеренными страницами. Это может заметно ускорить пререндер сайтов с useAsyncData или useFetch, когда одни и те же данные запрашиваются на разных страницах.
При необходимости функцию можно отключить.
export default defineNuxtConfig({
experimental: {
sharedPrerenderData: false,
},
})
Важно: при включении опции ключ данных должен однозначно соответствовать этим данным. Например, при запросе данных страницы через useAsyncData задавайте ключ, однозначно идентифицирующий данные. (useFetch делает это автоматически.)
// Так делать небезопасно на динамической странице (напр. `[slug].vue`): slug влияет на данные,
// но Nuxt этого не видит, так как ключ от него не зависит.
const route = useRoute()
const { data } = await useAsyncData(async (_nuxtApp, { signal }) => {
return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})
// Используйте ключ, однозначно идентифицирующий запрашиваемые данные.
const { data } = await useAsyncData(route.params.slug, async (_nuxtApp, { signal }) => {
return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})
clientNodeCompat
При включении Nuxt полифиллит импорты Node.js в клиентской сборке с помощью unenv.
Buffer работали в браузере, их нужно подключать вручную.import { Buffer } from 'node:buffer'
globalThis.Buffer ||= Buffer
scanPageMeta
Nuxt передаёт модулям на этапе сборки часть метаданных маршрута из definePageMeta (alias, name, path, redirect, props, middleware).
Работает только со статическими значениями или строками/массивами, не с переменными и условными присваиваниями. Подробнее: исходный issue.
По умолчанию метаданные страниц сканируются после регистрации всех маршрутов в pages:extend, затем вызывается хук pages:resolved.
При проблемах в проекте опцию можно отключить.
export default defineNuxtConfig({
experimental: {
scanPageMeta: false,
},
})
cookieStore
Включает поддержку CookieStore для отслеживания изменений cookies (если браузер поддерживает) и обновления ref из useCookie.
По умолчанию опция включена; при необходимости её можно отключить:
export default defineNuxtConfig({
experimental: {
cookieStore: false,
},
})
buildCache
Кэширует артефакты сборки Nuxt по хешу конфигурации и исходных файлов.
Учитываются только исходные файлы в srcDir и serverDir (части Vue и Nitro приложения).
По умолчанию отключено; при необходимости можно включить:
export default defineNuxtConfig({
experimental: {
buildCache: true,
},
})
При включении полная пересборка выполняется при изменении следующих файлов:
.nuxtrc
.npmrc
package.json
package-lock.json
yarn.lock
pnpm-lock.yaml
tsconfig.json
bun.lock
bun.lockb
Кроме того, любые изменения файлов в srcDir приводят к пересборке клиентского и серверного бандла Vue. Nitro пересобирается всегда (ведётся работа над тем, чтобы Nitro мог объявлять кэшируемые артефакты и их хеши).
checkOutdatedBuildInterval
Интервал (в мс) проверки наличия новых сборок. Не действует, если experimental.appManifest отключён.
Значение false отключает проверку.
export default defineNuxtConfig({
experimental: {
checkOutdatedBuildInterval: 3600000, // 1 hour, or false to disable
},
})
extraPageMetaExtractionKeys
Макрос definePageMeta() позволяет собирать метаданные страниц на этапе сборки. Nuxt предоставляет фиксированный набор поддерживаемых ключей для внутренних возможностей: редиректы, алиасы страниц, кастомные пути.
Эта опция задаёт дополнительные ключи для извлечения из метаданных страницы при использовании scanPageMeta.
<script lang="ts" setup>
definePageMeta({
foo: 'bar',
})
</script>
export default defineNuxtConfig({
experimental: {
extraPageMetaExtractionKeys: ['foo'],
},
hooks: {
'pages:resolved' (ctx) {
// ✅ foo is available
},
},
})
Модули могут получать из метаданных страницы дополнительные ключи в контексте сборки. При использовании в модуле рекомендуется также дополнить типы NuxtPage своими ключами.
navigationRepaint
Перед навигацией ожидается один кадр анимации, чтобы браузер успел перерисовать экран и зафиксировать действие пользователя.
Может снижать INP при навигации по предрендеренным маршрутам.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
navigationRepaint: false,
},
})
normalizeComponentNames
Nuxt приводит автоматически сгенерированные имена компонентов Vue к полному имени, которое используется при автоимпорте.
При возникновении проблем опцию можно отключить.
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: false,
},
})
По умолчанию Vue назначает компоненту имя по имени файла, если имя не задано вручную.
├─ components/
├─── SomeFolder/
├───── MyComponent.vue
В этом случае имя компонента в Vue будет MyComponent. Для использования с <KeepAlive> или отображения в Vue DevTools нужно использовать это имя.
Для автоимпорта при этом потребуется имя SomeFolderMyComponent.
При включении experimental.normalizeComponentNames оба имени совпадают: Vue генерирует имя компонента по правилам именования Nuxt.
normalizePageNames
Имена компонентов страниц приводятся в соответствие с именами маршрутов. На компоненты страниц устанавливается свойство __name, чтобы Vue мог корректно различать их в <KeepAlive> по имени.
По умолчанию Vue назначает компонентам имена по имени файла: например, у pages/foo/index.vue и pages/bar/index.vue будет имя index. Из-за этого фильтрация <KeepAlive> по имени ненадёжна — несколько страниц имеют одно имя.
При включённом normalizePageNames компоненты страниц получают имена по маршруту (например, foo и bar), поэтому можно использовать <KeepAlive> с include/exclude без ручного добавления defineOptions({ name: '...' }) на каждой странице.
Опция включается при future.compatibilityVersion: 5 и выше; при необходимости её можно отключить:
export default defineNuxtConfig({
experimental: {
normalizePageNames: false,
},
})
<template>
<NuxtPage :keepalive="{ include: ['foo'] }" />
</template>
spaLoadingTemplateLocation
При рендере страницы только для клиента (ssr: false) при необходимости показывается экран загрузки из ~/spa-loading-template.html.
Значение within встраивает шаблон так:
<div id="__nuxt">
<!-- spa loading template -->
</div>
Либо шаблон можно вывести рядом с корнем приложения Nuxt, задав значение body:
<div id="__nuxt"></div>
<!-- spa loading template -->
Это уменьшает белую вспышку при гидрации страницы только для клиента.
browserDevtoolsTiming
Включает маркеры производительности для хуков Nuxt в инструментах разработчика. Их можно смотреть во вкладке Performance в браузерах на Chromium — удобно для отладки и оптимизации.
По умолчанию включено в режиме разработки; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
browserDevtoolsTiming: false,
},
})
debugModuleMutation
Записывает изменения nuxt.options в контексте модулей — помогает отлаживать правки конфигурации при инициализации Nuxt.
По умолчанию включено при включённом режиме debug; при необходимости можно отключить.
Чтобы включить явно:
export default defineNuxtConfig({
experimental: {
debugModuleMutation: true,
},
})
lazyHydration
Включает стратегии гидрации для компонентов <Lazy>: гидрация откладывается до момента необходимости, что улучшает производительность.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
lazyHydration: false,
},
})
templateImportResolution
Отключает разрешение импортов в шаблонах Nuxt относительно пути модуля, добавившего шаблон.
По умолчанию Nuxt разрешает импорты в шаблонах относительно модуля, который их добавил. Значение false отключает это поведение — может пригодиться при конфликтах разрешения в некоторых средах.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
templateImportResolution: false,
},
})
templateRouteInjection
По умолчанию объект маршрута из автоимпортируемого композабла useRoute() синхронизирован с текущей страницей в <NuxtPage>. Для экспортируемого vue-router'ом useRoute и для объекта $route в шаблонах Vue это не так.
При включении этой опции подмешивается миксин, который синхронизирует объект $route в шаблоне с управляемым Nuxt useRoute().
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
templateRouteInjection: false,
},
})
decorators
Включает синтаксис декораторов во всём приложении Nuxt/Nitro.
При сборщике Vite (по умолчанию) декораторы обрабатываются через Babel и @babel/plugin-proposal-decorators. При webpack или rspack — через esbuild.
В TypeScript давно есть поддержка декораторов через compilerOptions.experimentalDecorators — это реализация до стандартизации TC39. Сейчас декораторы — предложение Stage 3, в TS 5.0+ поддерживаются без отдельной настройки (см. https://github.com/microsoft/TypeScript/pull/52582 и https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators).
Включение experimental.decorators даёт поддержку предложения TC39, а не старой реализации TypeScript compilerOptions.experimentalDecorators.
Использование
export default defineNuxtConfig({
experimental: {
decorators: true,
},
})
При использовании сборщика Vite или серверной сборки Nitro потребуются дополнительные пакеты Babel в dev-зависимостях:
npm install -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
pnpm add -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
yarn add -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
function something (_method: () => unknown) {
return () => 'decorated'
}
class SomeClass {
@something
public someMethod () {
return 'initial'
}
}
const value = new SomeClass().someMethod()
// this will return 'decorated'
defaults
Задаёт опции по умолчанию для основных компонентов и композаблов Nuxt.
В будущем эти опции, вероятно, перенесут (например, в app.config или в каталог app/).
export default defineNuxtConfig({
experimental: {
defaults: {
nuxtLink: {
componentName: 'NuxtLink',
prefetch: true,
prefetchOn: {
visibility: true,
},
},
useAsyncData: {
deep: true,
},
useState: {
resetOnClear: true,
},
},
},
})
Опция useState.resetOnClear задаёт, сбрасывает ли clearNuxtState состояние в начальное значение (из функции init в useState) вместо undefined. По умолчанию true при compatibilityVersion: 5.
purgeCachedData
Очищать ли кэши Nuxt (static и asyncData) при навигации по маршрутам.
Nuxt автоматически очищает кэш из useAsyncData и nuxtApp.static.data. Это снижает утечки памяти и даёт актуальные данные при необходимости, но поведение можно отключить.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
purgeCachedData: false,
},
})
prefetchPreloadTags
Если <NuxtLink> префетчится и у целевого маршрута включено извлечение payload (по умолчанию для пререндеренных и кэшируемых маршрутов), подсказки <link rel="preload">, которые целевая страница задала через useHead (или модули вроде <NuxtImg preload> из @nuxt/image), переносятся в текущий документ.
Перенесённые ссылки понижаются с rel="preload" до rel="prefetch", чтобы не конкурировать с критичными ресурсами текущей страницы. Переносятся только пользовательские теги head; preload чанков JS/CSS на этапе сборки уже обрабатываются отдельно в пайплайне префетча.
Флаг по умолчанию выключен: вместе с prefetchOn: 'visibility' (значение по умолчанию у <NuxtLink>) это может запустить много кросс-маршрутных префетчей сразу. Включайте его, когда уверены, что preload на целевых страницах стоит переносить для ссылок, с которыми обычно взаимодействуют пользователи.
export default defineNuxtConfig({
experimental: {
prefetchPreloadTags: true,
},
})
granularCachedData
Вызывать ли и использовать ли результат getCachedData при обновлении данных для useAsyncData и useFetch (через watch, refreshNuxtData() или ручной вызов refresh()).
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
granularCachedData: false,
},
})
headNext
Оптимизации для работы с head:
- Подключается плагин capo.js для более эффективного рендера тегов в
<head>. - Используется плагин hash hydration для снижения нагрузки при начальной гидрации.
По умолчанию включено; при необходимости можно отключить:
export default defineNuxtConfig({
experimental: {
headNext: false,
},
})
pendingWhenIdle
Для useAsyncData и useFetch: должно ли pending быть true, пока загрузка данных ещё не начата.
По умолчанию отключено; при необходимости можно включить:
export default defineNuxtConfig({
experimental: {
pendingWhenIdle: true,
},
})
entryImportMap
По умолчанию Nuxt повышает стабильность чанков, разрешая entry-чанк бандла через import map.
В начало тега <head> добавляется import map:
<script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script>
В чанках скриптов, выдаваемых Vite, импорты идут из #entry. Изменения entry-файла не инвалидируют остальные неизменённые чанки.
vite.build.target указан браузер без поддержки import maps или если vite.build.rolldownOptions.output.entryFileNames задан без [hash].Отключить эту возможность можно так:
export default defineNuxtConfig({
experimental: {
entryImportMap: false,
},
// или лучше задать нужный target в vite — Nuxt его учтёт
vite: {
build: {
target: 'safari13',
},
},
})
typescriptPlugin
Включает улучшенный опыт разработки с TypeScript через модуль @dxup/nuxt.
Экспериментальный плагин даёт лучшую интеграцию TypeScript и инструменты для удобной работы с TypeScript в Nuxt.
По умолчанию отключено; при необходимости можно включить:
export default defineNuxtConfig({
experimental: {
typescriptPlugin: true,
},
})
- Иметь
typescriptв зависимостях - Настроить VS Code на использование TypeScript из workspace (см. документацию VS Code)
ssrStreaming
Включает потоковый SSR (streaming), чтобы значительно улучшить Time to First Byte (TTFB). Когда опция включена, сервер сразу отправляет HTML-оболочку (включая <head>, стили, подсказки предзагрузки и входные скрипты), а затем постепенно передаёт отрендеренное тело страницы через renderToWebStream из Vue.
export default defineNuxtConfig({
experimental: {
ssrStreaming: true,
},
})
Стриминг автоматически отключается для ботов и поисковых роботов (Googlebot, Bingbot и т.д.), чтобы поисковые системы получали полностью отрендеренный HTML и SEO не страдало. Шаблон по умолчанию охватывает только индексирующих роботов; Lighthouse и другие инструменты аудита намеренно под него не попадают, чтобы синтетические измерения отражали тот же потоковый ответ, что и у реальных пользователей. Регулярное выражение для определения ботов можно настроить:
export default defineNuxtConfig({
experimental: {
ssrStreaming: {
botRegex: /googlebot|bingbot|my-internal-crawler/i,
},
},
})
Стримингом также можно управлять для каждого маршрута через routeRules:
export default defineNuxtConfig({
experimental: {
ssrStreaming: true,
},
routeRules: {
'/no-stream/**': { streaming: false },
},
})
routeRulesзадаёт для маршрутаnoScripts,cache,isr,swr,redirectилиstreaming: false- Маршруты с
ssr: false(уже отрендеренные как SPA) - Боты и поисковые роботы (управляется через
botRegex) - Пререндеренные маршруты (
nuxi generate) - Серверные редиректы
navigateTo()из плагинов, middleware или setup страницы - Фатальные ошибки при первичном рендеринге (до отправки оболочки)
- Доходит до клиента: изменения из плагинов Nuxt и Nitro, которые полностью отрабатывают до начала рендеринга.
- Отбрасывается: вызовы
setResponseStatus(),useResponseHeader(), записиuseCookie()и вызовы h3setHeader()/appendResponseHeader()во время рендеринга компонентов (в том числе послеawaitв route middleware или<script setup>), поскольку это происходит, когда оболочка уже ушла по сети.
routeRules: { '/path': { streaming: false } }— статически, для маршрута.- Хук
render:routeсctx.prefersStream = false— в рантайме, для отдельного запроса (например, для маршрутов, которые условно возвращают 404).
payload.error, а закрывающие теги всё равно отправляются как корректный документ — клиент подхватывает ошибку при гидратации и рендерит страницу ошибки. Ошибки, выброшенные до отправки оболочки, попадают в буферизованный рендерер ошибок с правильным кодом статуса.<head> несёт только стили и подсказки входного чанка. Как только рендеринг зарегистрирует модули страницы и макета, их CSS стримится сразу за оболочкой (встроенным <style> при включённом inlineStyles, иначе ссылками на стили), поэтому стили страницы, макета и async-компонентов верхнего уровня приходят до отрисовки тела (вложенные async-компоненты — отдельный случай FOUC, о нём ниже). JS-чанки конкретного маршрута из оболочки не предзагружаются; браузер находит их после разбора входного скрипта. Стриминг улучшает TTFB на любом маршруте; выигрыш по LCP наибольший на маршрутах, чей JS пересекается с входным чанком.nuxt-client) обычно вставляются в HTML отдельным проходом после рендеринга, что невозможно, когда тело уже прошло мимо якорей островков в потоке. Вместо этого рендерер выводит каждый teleport островка как инертный <template> в конце документа и перемещает его на место inline-скриптом, который выполняется до гидратации. Для кода приложения это прозрачно. Исключение — приложения, собранные с features.noScripts и компонентами-островками: они откатываются к буферизованному рендереру, поскольку скрипт перемещения не может выполниться.Хуки модулей
Модули участвуют в потоковом ответе через существующий хук render:html (теперь со флагом streaming: true во втором аргументе), а также через хук принятия решения на каждый запрос и два хука только для стриминга:
render:routeсрабатывает один раз на запрос перед началом рендеринга, для любого рендеринга (со стримингом или без). Прочитайтеctx.canStream, чтобы узнать, возможен ли стриминг для маршрута, и задайтеctx.prefersStream = false, чтобы принудительно использовать буферизованный рендеринг для этого запроса — например, по куке, статусу авторизации или A/B-группе. Рендерер стримит только приcanStream && prefersStream. Это рантайм-обходной путь для статической конфигурацииrouteRules/botRegex.render:htmlсрабатывает один раз, до отправки оболочки, со флагомstreaming: trueво втором аргументе. ИзмененияhtmlAttrs,head,bodyAttrsиbodyPrependдоходят по сети. Измененияbody/bodyAppendотбрасываются, поскольку тело вот-вот начнёт стримиться (в режиме разработки выводится предупреждение). Модули, которые меняют только поля head (внедрение CSP, OG-теги, meta для аналитики), работают со стримингом без изменений кода.render:html:chunkсрабатывает для каждого чанка, произведённого рендерером, перед постановкой его в очередь. Меняйтеctx.chunk: Uint8Array, чтобы преобразовать байты (например, внедрить nonce); читайтеctx.index, чтобы отличить первый чанк от последующих.render:html:closeсрабатывает после завершения потока тела, перед закрывающими тегами. Меняйтеctx.bodyAppend: string[], чтобы вставить финальную разметку (теги аналитики в конце body, серверные отладочные виджеты и т.д.).
// modules/streaming-csp/src/runtime/server-plugin.ts
import { defineNitroPlugin } from '#imports'
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('render:html', (ctx, { event }) => {
const nonce = event.context.cspNonce
if (!nonce) { return }
// Работает и для стриминга (до оболочки), и для буферизованного пути (после рендеринга).
for (let i = 0; i < ctx.head.length; i++) {
ctx.head[i] = ctx.head[i].replace(/<script(?![^>]*\snonce=)/g, `<script nonce="${nonce}"`)
}
})
})
<style> маршрута. Если на отрендеренных скриптах head есть nonce, рендерер автоматически переиспользует его на всех них, поэтому строгая политика script-src/style-src 'nonce-…' не блокирует стриминг. Модулю достаточно проставить nonce на скриптах head (как выше); хук render:html:chunk остаётся доступным для проставления nonce на скриптах, которые компоненты рендерят в тело.<style> из SFC как JavaScript-модули, которые внедряют стили на клиенте после выполнения модуля, без соответствующего <link> в оболочке. При стриминге браузер начинает отрисовывать переданный DOM ещё до выполнения этих модулей-инжекторов, поэтому стили из SFC на короткое время мигают без оформления.Обходной путь: вынесите критичные для отрисовки стили в глобальный CSS-файл, подключённый через css: ['~/assets/main.css']. Глобальные CSS-файлы выводятся как <link rel="stylesheet"> в <head> оболочки и применяются до того, как начнёт стримиться тело. Блоки <style> в SFC по-прежнему подходят для стилей уровня компонента, которые не влияют на первую отрисовку.Продакшен-сборки извлекают все стили в настоящие CSS-файлы (или встраивают через features.inlineStyles), поэтому это касается только nuxt dev. Проверяйте потоковую отрисовку через nuxt build && nuxt preview.<Suspense> (Vue создаёт их сразу при старте рендеринга).Async-компонент, который рендерится внутри другого async-компонента, создаётся только после разрешения родителя — уже после того, как первый чанк ушёл в поток. Его <style> из SFC не попадает в чанк стилей после оболочки и вместо этого выводится в закрывающем HTML, за DOM самого компонента. Браузер отрисовывает такой компонент без стилей, пока не придёт финальный чанк.Чтобы избежать этого, держите критичные для отрисовки стили вне глубоко вложенных async-компонентов:- Выносите стили, влияющие на первую отрисовку, в глобальный CSS-файл (
css: ['~/assets/main.css']); они попадают в<head>оболочки. - Используйте утилитарные классы (Tailwind, UnoCSS): утилитарный CSS живёт во входном файле стилей, а не в блоках
<style>отдельных компонентов. - Держите async-компоненты с критичным для отрисовки
<style>прямо под границей<Suspense>, а не вложенными за другим async-родителем. - Либо отключите стриминг для маршрута через
routeRules: { '/path': { streaming: false } }.