Экспериментальные возможности
В 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
Вместо элементов <div> в SSR для client-only компонентов используются узлы-комментарии (<!--placeholder-->).
При включении .client.vue и обёртки createClientOnly() на сервере рендерят HTML-комментарий вместо пустого <div>. Это устраняет проблему гидратации Vue: scoped-стили могут не применяться, когда placeholder-<div> и корень компонента имеют одинаковый тег.
class, style и т.д.), переданные в .client.vue, не попадут в SSR HTML. Если нужны стилизованные placeholder'ы против layout shift, используйте <ClientOnly> со слотом #fallback.Флаг включается при future.compatibilityVersion 5 и выше, но его можно включить явно:
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-стриминг для улучшения Time to First Byte (TTFB). Сервер сразу отправляет HTML-оболочку (<head>, стили, preload-подсказки и entry-скрипты), затем постепенно стримит тело через renderToWebStream Vue.
export default defineNuxtConfig({
experimental: {
ssrStreaming: true,
},
})
Стриминг автоматически отключается для ботов и краулеров (Googlebot, Bingbot и т.д.), чтобы поисковики получали полностью отрендеренный HTML. Паттерн по умолчанию ловит только индексирующих краулеров; Lighthouse и другие аудит-инструменты в него не входят, чтобы синтетические замеры отражали тот же streamed-ответ, что и у пользователей. Regex детекции ботов можно переопределить:
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) - user agent бота/краулера (через
botRegex) - пререндеренные маршруты (
nuxi generate) - серверные редиректы
navigateTo()из плагинов, middleware или setup страницы - фатальные ошибки до flush оболочки
- Доходит до клиента: мутации из плагинов Nuxt и Nitro, которые выполняются до начала рендера.
- Теряется:
setResponseStatus(),useResponseHeader(), записьuseCookie()и вызовы h3setHeader()/appendResponseHeader()во время рендера компонентов (в том числе послеawaitв route middleware или<script setup>), потому что оболочка уже ушла в сеть.
routeRules: { '/path': { streaming: false } }: статически, по маршруту.- хук
render:routeсctx.prefersStream = false: в рантайме, по запросу (например, для условного 404).
payload.error, а закрывающие теги всё равно отправляются как валидный документ — клиент подхватит ошибку при гидратации и покажет error page. Ошибки до flush оболочки обрабатывает буферизованный error renderer с корректным статусом.<head> содержит только стили и подсказки entry-чанка. После регистрации модулей страницы и layout их CSS стримится сразу после оболочки (inline как <style> при inlineStyles, иначе как <link>), так что стили страницы, layout и top-level async-компонентов приходят до отрисовки body (вложенные async-компоненты — отдельный FOUC-кейс ниже). Route-specific JS-чанки из оболочки не прелоадятся; браузер находит их после парсинга entry-скрипта. Стриминг улучшает TTFB на любом маршруте; выигрыш по LCP максимален там, где JS маршрута пересекается с entry-чанком.nuxt-client) компоненты обычно вшиваются в HTML в post-render проходе, что невозможно после того, как body прошёл мимо якорей island. Вместо этого рендерер выводит каждый island teleport как inert <template> в конце документа и переносит его на место inline-скриптом до гидратации. Для app-кода это прозрачно. Исключение — сборки с features.noScripts и island-компонентами: fallback на буферизованный рендер, потому что relocation-скрипт не выполнится.Хуки модулей
Модули участвуют в streamed-ответе через существующий хук render:html (теперь с флагом streaming: true во втором аргументе), хук решения по запросу и два хука только для стриминга:
render:routeсрабатывает один раз на запрос до рендера, для любого режима. Читайтеctx.canStream, чтобы понять, доступен ли стриминг для маршрута, и ставьтеctx.prefersStream = false, чтобы принудительно включить буферизацию (cookie, auth, A/B). Рендерер стримит только приcanStream && prefersStream. Это runtime-escape для статическогоrouteRules/botRegex.render:htmlсрабатывает один раз до flush оболочки сstreaming: trueво втором аргументе. МутацииhtmlAttrs,head,bodyAttrsиbodyPrependдоходят до клиента. Мутацииbody/bodyAppendтеряются, потому что body вот-вот начнёт стримиться (в dev будет предупреждение). Модули, которые меняют только head (CSP, OG, analytics meta), работают без изменений кода.render:html:chunkсрабатывает на каждый чанк рендерера до постановки в очередь. Меняйтеctx.chunk: Uint8Array(nonce injection); поctx.indexотличайте первый чанк от следующих.render:html:closeпосле завершения body stream, до закрывающих тегов. Меняйтеctx.bodyAppend: string[]для финальной разметки (analytics в конце body, debug-виджеты и т.д.).
// 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 }
// Работает и для streaming (pre-shell), и для buffered (post-render).
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, renderer переиспользует его для всех — strict script-src/style-src 'nonce-…' не блокирует стриминг. Модулю достаточно проставить nonce на head-скриптах (как выше); хук render:html:chunk остаётся для скриптов, которые компоненты рендерят в body.<style> из SFC как JS-модули, которые инжектят стили на клиенте после evaluate, без <link> в оболочке. При стриминге браузер начинает рисовать DOM до выполнения этих модулей — SFC-стили на миг без оформления.Обход: критичные для первой отрисовки стили положите в глобальный CSS через css: ['~/assets/main.css']. Глобальные файлы попадают в <link rel="stylesheet"> в <head> оболочки и применяются до стрима body. SFC <style> подходят для scoped-стилей, которые не блокируют первый paint.В production все стили извлекаются в CSS-файлы (или inline через features.inlineStyles), поэтому это касается только nuxt dev. Проверяйте стриминг через nuxt build && nuxt preview.<Suspense> (Vue создаёт их eagerly при старте рендера).Async-компонент внутри другого async-компонента создаётся только после resolve родителя, когда первый чанк уже ушёл. Его SFC <style> не попадает в post-shell styles chunk и уезжает в closing HTML за DOM компонента. Браузер рисует его без стилей до финального чанка.Держите paint-critical стили вне глубоко вложенных async-комponentов:- Стили, блокирующие первый paint, — в глобальном CSS (
css: ['~/assets/main.css']); они попадают в<head>оболочки. - Utility-классы (Tailwind, UnoCSS): utility CSS в entry stylesheet, не в per-component
<style>. - Async-компоненты с paint-critical
<style>— прямо под<Suspense>, а не за другим async-родителем. - Или отключите стриминг:
routeRules: { '/path': { streaming: false } }.