实验特性

启用 Nuxt 实验性功能以解锁新可能性。

Nuxt 包含可以在配置文件中启用的实验性功能。

在内部,Nuxt 使用 @nuxt/schema 来定义这些实验性功能。您可以参考 API 文档源码 以获取更多信息。

请注意,这些功能是实验性的,未来可能会被移除或修改。

alwaysRunFetchOnKeyChange

当 key 发生变化时,是否运行 useFetch,即便其设置为 immediate: false 且尚未被触发。

如果 immediate: true 或已被触发,useFetchuseAsyncData 在 key 更改时将始终运行。

此标志默认禁用,但您可以启用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    alwaysRunFetchOnKeyChange: true,
  },
})

appManifest

Use the app manifest to have the client follow route rules.

This flag is enabled by default, but you can disable this feature:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    appManifest: false,
  },
})

asyncContext

启用原生异步上下文,使其可在 Nuxt 和 Nitro 中被嵌套的 composable 访问。这使得在异步 composable 中使用 composable 成为可能,并减少出现 Nuxt instance is unavailable 错误的概率。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    asyncContext: true,
  },
})
在 GitHub pull-request 上查看完整说明。

asyncEntry

为 Vue 包生成异步入口点,以利于支持模块联邦(module federation)。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    asyncEntry: true,
  },
})

externalVue

在构建时将 vue@vue/*vue-router 外部化。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    externalVue: false,
  },
})
此功能很可能在不久的将来被移除。

extractAsyncDataHandlers

useAsyncDatauseLazyAsyncData 调用中提取处理函数到单独的块,以提高代码拆分和缓存效率。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    extractAsyncDataHandlers: true,
  },
})

此功能将内联处理函数转换为动态导入的代码块:

<!-- 转换前 -->
<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

在加载 vite/webpack chunk 时发生错误时发出 app:chunkError 钩子。默认行为是在导航到新路由时,当 chunk 无法加载时重新加载新路由。

默认情况下,Nuxt 在导航到新路由时,如果 chunk 无法加载也会执行路由重载(automatic)。

设置为 automatic-immediate 将使 Nuxt 在 chunk 无法加载时立即对当前路由执行重载(而不是等待导航)。这对非导航触发的 chunk 错误很有用,例如当 Nuxt 应用无法加载懒加载组件。这种行为的一个潜在缺点是可能会导致不希望的重载,例如当应用并不需要导致错误的那部分 chunk 时。

您可以将其设置为 false 来禁用自动处理,或设置为 manual 以手动处理 chunk 错误。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    emitRouteChunkError: 'automatic', // 或 'automatic-immediate', 'manual' 或 false
  },
})

enforceModuleCompatibility

When Nuxt modules are incompatible, should an error be thrown and loading blocked.

This feature is disabled by default.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    enforceModuleCompatibility: true,
  },
})

restoreState

允许在发生 chunk 错误或手动调用 reloadNuxtApp() 后,从 sessionStorage 恢复 Nuxt 应用状态。

为了避免水合(hydration)错误,此操作仅在 Vue 应用挂载后应用,这意味着初次加载时可能会有闪烁现象。

在启用此项之前请谨慎考虑,因为它可能导致意外行为,并考虑为 useState 提供显式键,因为自动生成的键在不同构建间可能不匹配。
nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    restoreState: true,
  },
})

inlineRouteRules

Use defineRouteRules to define route rules at the page level.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    inlineRouteRules: true,
  },
})

Create matching route rules based on the page path.

Read more in the defineRouteRules utility.
Docs > 4 X > Guide > Concepts > Rendering#hybrid Rendering 中查看详情

renderJsonPayloads

允许以支持复原复杂类型的方式渲染 JSON 负载。

此标志默认已启用,但你可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    renderJsonPayloads: false,
  },
})

noVueServer

在 Nitro 中禁用 Vue 服务端渲染器端点。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    noVueServer: true,
  },
})

parseErrorData

在渲染服务器错误页面时是否解析 error.data

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    parseErrorData: false,
  },
})

payloadExtraction

控制 payload 数据在预渲染(prerendered)和缓存(ISR/SWR)页面中如何交付。

  • 'client' - 初始服务端渲染时,Payload 会内联在 HTML 中,并在客户端导航时提取到 _payload.json 文件中。
  • true - 初始服务端渲染和客户端导航都会将 Payload 提取到单独的 _payload.json 文件中。
  • false - 完全禁用 Payload 提取。Payload 始终以内联形式包含在 HTML 中,并且不会生成 _payload.json 文件。

默认值为 true;当设置了 compatibilityVersion: 5 时,默认值为 'client'。当设置了 ssr: false 时,会强制为 false

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: 'client',
  },
})
阅读更多关于 payload 提取以及每种模式后果的内容。

clientNodePlaceholder

使用注释节点(<!--placeholder-->)替代 <div> 元素,作为服务器端渲染(SSR)期间仅客户端组件的占位符。

启用后,.client.vue 组件以及 createClientOnly() 包装器将在服务器上渲染 HTML 注释,而不是空的 <div>。这修复了一个 Vue hydration 问题:当占位 <div> 和实际组件根节点共享相同的标签名时,可能导致作用域样式未被应用。

启用此选项意味着传递给 .client.vue 组件的属性(classstyle 等)将不会出现在 SSR 的 HTML 中。如果你需要带样式的占位以防止布局抖动,请使用带 #fallback 插槽的 <ClientOnly>

future.compatibilityVersion 设置为 5 或更高时启用此标志,但你也可以显式启用:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    clientNodePlaceholder: true,
  },
})

clientFallback

启用实验性的 <NuxtClientFallback> 组件,这样当发生 SSR 错误时,内容就可以在客户端渲染。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    clientFallback: true,
  },
})

crossOriginPrefetch

使用 Speculation Rules API 启用跨域预取(cross-origin prefetch)。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    crossOriginPrefetch: true,
  },
})
阅读有关 Speculation Rules API 的更多信息。

viewTransition

启用与客户端路由器的 View Transition API 集成。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    viewTransition: true,
  },
})

你也可以传入一个对象来配置 视图过渡类型,它允许基于导航类型使用不同的 CSS 动画:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    viewTransition: {
      enabled: true,
      types: ['slide'],
    },
  },
})
了解有关 View Transition API 的更多信息。
了解更多关于 View Transition API 的内容。

writeEarlyHints

在使用 Node 服务器时启用写入 early hints。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    writeEarlyHints: true,
  },
})

componentIslands

使用 <NuxtIsland>.island.vue 文件启用实验性的组件岛(component islands)支持。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    componentIslands: true, // 也可为 false 或 'local+remote'
  },
})
Docs > 4 X > Directory Structure > App > Components#server Components 中查看详情
在非 SFC 组件上跳过 nuxt-client,例如 <NuxtLink>。参见 server components 中的客户端组件
你可以在 GitHub 上关注服务器组件的路线图。

localLayerAliases

Resolve the ~, ~~, @, and @@ aliases located within a layer relative to that layer’s source and root directories.

This flag is enabled by default, but you can disable it:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    localLayerAliases: false,
  },
})

typedPages

启用新的实验性类型化路由。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    typedPages: true,
  },
})

开箱即用,这将为 navigateTo<NuxtLink>router.push() 等提供类型支持。

你甚至可以通过在页面中使用 const route = useRoute('route-name') 来获得类型化的 params。

watcher

设置将用作 Nuxt 监视服务的替代 watcher。

Nuxt 默认使用 chokidar-granular,它会忽略被排除在监视之外的顶级目录(例如 node_modules.git)。

你可以将其设置为 parcel 以使用 @parcel/watcher,这在大型项目或 Windows 平台上可能会提高性能。

你也可以将其设置为 chokidar 来监视源目录中的所有文件。

将其设为 'builder' 以复用当前构建器自身的文件监视器(例如 Vite 的 server.watcher),而不是启动第二个监视器。这会减少开发模式下活动的文件监视器数量,并且在 future.compatibilityVersion5 时会成为默认值。如果当前构建器没有实现自己的监视器(目前是 webpack 和 rspack),Nuxt 会记录警告并回退到默认选择。

将其设为 'builder' 以复用当前构建器自身的文件监视器(例如 Vite 的 server.watcher),而不是启动第二个监视器。这会减少开发模式下活动的文件监视器数量,并且在 future.compatibilityVersion5 时会成为默认值。如果当前构建器没有实现自己的监视器(目前是 webpack 和 rspack),Nuxt 会记录警告并回退到默认选择。

将其设为 'builder' 以复用当前构建器自身的文件监视器(例如 Vite 的 server.watcher),而不是启动第二个监视器。这会减少开发模式下活动的文件监视器数量,并且在 future.compatibilityVersion5 时会成为默认值。如果当前构建器没有实现自己的监视器(目前是 webpack 和 rspack),Nuxt 会记录警告并回退到默认选择。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    watcher: 'chokidar-granular', // 也可以使用 'chokidar'、'parcel' 或 'builder'
  },
})

sharedPrerenderData

Nuxt 会自动在预渲染的页面之间共享 payload 数据。当预渲染使用 useAsyncDatauseFetch 且在不同页面中获取相同数据时,这可以显著提高性能。

如果需要,您可以禁用此功能。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false,
  },
})

启用此功能时特别重要的是确保您的数据的任何唯一键总是可解析为相同的数据。例如,如果您在动态页面中使用 useAsyncData 来获取与特定页面相关的数据,则应提供一个唯一匹配该数据的键。(useFetch 应会为您自动做到这一点。)

// 这在动态页面(例如 `[slug].vue`)中不安全,因为路由 slug 会影响获取的数据
// 但 Nuxt 因为不反映在 key 中,因此无法识别差异。
const route = useRoute()
const { data } = await useAsyncData(async (_nuxtApp, { signal }) => {
  return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})
// 相反,您应使用一个唯一标识所获取数据的 key。
const { data } = await useAsyncData(route.params.slug, async (_nuxtApp, { signal }) => {
  return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})

clientNodeCompat

启用此功能后,Nuxt 将在客户端构建中使用 unenv 自动为 Node.js 导入提供 polyfill。

要使像 Buffer 这样的全局变量在浏览器中工作,您需要手动注入它们。
import { Buffer } from 'node:buffer'

globalThis.Buffer ||= Buffer

scanPageMeta

Nuxt 在构建时向模块公开在 definePageMeta 中定义的一些路由元数据(特别是 aliasnamepathredirectpropsmiddleware)。

这仅适用于静态值或字符串/数组,而不适用于变量或条件赋值。有关更多信息和上下文,请参阅原始问题

默认情况下,页面元数据仅在 pages:extend 中所有路由注册完成后才会被扫描。然后会调用另一个钩子 pages:resolved

如果此功能在您的项目中造成问题,您可以禁用它。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    scanPageMeta: false,
  },
})

cookieStore

启用 CookieStore 支持以监听 cookie 更新(如果浏览器支持)并刷新 useCookie 的 ref 值。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    cookieStore: false,
  },
})
阅读有关 CookieStore 的更多信息。

buildCache

基于配置和源文件的哈希缓存 Nuxt 构建产物。

这仅适用于 srcDirserverDir 中的源文件,用于您应用的 Vue/Nitro 部分。

此标志默认禁用,但您可以启用它:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    buildCache: true,
  },
})

启用后,以下文件的更改将触发完全重建:

Directory structure
.nuxtrc
.npmrc
package.json
package-lock.json
yarn.lock
pnpm-lock.yaml
tsconfig.json
bun.lock
bun.lockb

此外,srcDir 中任何文件的更改都将触发 Vue 客户端/服务端包的重建。Nitro 将始终被重建(不过正在进行工作以允许 Nitro 声明其可缓存的产物及其哈希)。

最多保留 10 个缓存 tarball。

checkOutdatedBuildInterval

设置检查新构建的时间间隔(以毫秒为单位)。当 experimental.appManifestfalse 时此功能被禁用。

设置为 false 可禁用该检查。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    checkOutdatedBuildInterval: 3600000, // 1 小时,或 false 表示禁用
  },
})

extraPageMetaExtractionKeys

definePageMeta() 宏是收集页面构建时元数据的有用方式。Nuxt 本身提供了一组受支持的键列表,用于驱动一些内部功能,例如重定向、页面别名和自定义路径。

此选项允许在使用 scanPageMeta 时传入额外的键以从页面元数据中提取。

<script lang="ts" setup>
definePageMeta({
  foo: 'bar',
})
</script>
export default defineNuxtConfig({
  experimental: {
    extraPageMetaExtractionKeys: ['foo'],
  },
  hooks: {
    'pages:resolved' (ctx) {
      // ✅ 现在可以访问 foo
    },
  },
})

这允许模块在构建上下文中访问页面元数据中的额外元信息。如果您在模块中使用此功能,建议也通过您的键扩展 NuxtPage 类型

在导航之前等待一个动画帧,让浏览器有机会在响应用户交互时重新绘制。

在导航到预渲染路由时,这可以降低 INP。

此标志默认启用,但你可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    navigationRepaint: false,
  },
})

normalizeComponentNames

Nuxt 会更新自动生成的 Vue 组件名称,以匹配用于自动导入组件时的完整组件名。

如果遇到问题,您可以禁用此功能。

nuxt.config.ts
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.vuepages/bar/index.vue 都会被命名为 index。这种情况让基于名称的 <KeepAlive> 过滤变得不可靠,因为多个页面共享相同的名称。

开启 normalizePageNames 后,页面组件将以路由名称命名(例如 foobar),这样您就可以使用 <KeepAlive>include/exclude 功能,而不用为每个页面手动添加 defineOptions({ name: '...' })

future.compatibilityVersion 设置为 5 或更高时,此标志默认启用,但您可以禁用该功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    normalizePageNames: false,
  },
})
app.vue
<template>
  <NuxtPage :keepalive="{ include: ['foo'] }" />
</template>

spaLoadingTemplateLocation

在渲染仅客户端页面(ssr: false)时,可选地渲染一个加载屏幕(来自 ~/spa-loading-template.html)。

可以将其设置为 within,将按如下方式渲染:

<div id="__nuxt">
  <!-- spa 加载模板 -->
</div>

或者,您可以通过将其设置为 body 将模板与 Nuxt 应用根并列渲染:

<div id="__nuxt"></div>
<!-- spa 加载模板 -->

这可以避免在为仅客户端页面执行水合时出现白屏闪烁。

browserDevtoolsTiming

为浏览器开发者工具启用 Nuxt 钩子的性能标记(performance markers)。这会在基于 Chromium 的浏览器的 Performance 选项卡中添加可跟踪的性能标记,便于调试和优化性能。

在开发模式下默认启用此功能。如果需要禁用,可以这样做:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    browserDevtoolsTiming: false,
  },
})
查看 PR #29922 以获取实现细节。
了解有关 Chrome DevTools Performance API 的更多信息。

debugModuleMutation

记录模块上下文中对 nuxt.options 的变更,以帮助调试 Nuxt 初始化阶段模块所做的配置更改。

当启用 debug 模式时默认启用此功能。如果需要禁用,也可以这样做:

要显式启用它:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    debugModuleMutation: true,
  },
})
查看 PR #30555 以获取实现细节。

延迟水合

<Lazy> 组件启用延迟水合(hydration)策略,通过延迟组件的水合来提高性能。

默认启用延迟水合,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    lazyHydration: false,
  },
})
阅读有关延迟水合的更多内容。

templateImportResolution

禁用从添加模板的模块的路径解析 Nuxt 模板中的导入。

默认情况下,Nuxt 会尝试将模板中的导入相对于添加它们的模块进行解析。将此设置为 false 会禁用该行为,这在某些环境中遇到解析冲突时可能有用。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    templateImportResolution: false,
  },
})
查看 PR #31175 以获取实现细节。

templateRouteInjection

默认情况下,自动导入的 useRoute() composable 返回的路由对象会与 <NuxtPage> 中当前可见页面保持同步。vue-router 导出的 useRoute 或 Vue 模板中可用的默认 $route 对象则不然。

启用此选项将注入一个混入,以使模板中的 $route 对象与 Nuxt 管理的 useRoute() 保持同步。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    templateRouteInjection: false,
  },
})

decorators

此选项可在整个 Nuxt/Nitro 应用中启用装饰器语法。

当使用 Vite 构建器(默认)时,装饰器会通过 Babel 降级(lowered)为使用 @babel/plugin-proposal-decorators。当使用 webpack 或 rspack 构建器时,装饰器会通过 esbuild 降级。

长期以来,TypeScript 通过 compilerOptions.experimentalDecorators 支持装饰器。该实现早于 TC39 标准化过程。现在,装饰器已成为一个 Stage 3 提案,并在 TS 5.0+ 中无需特殊配置即可支持(参见 https://github.com/microsoft/TypeScript/pull/52582https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators)。

启用 experimental.decorators 是启用对 TC39 提案的支持,不是 启用 TypeScript 之前的 compilerOptions.experimentalDecorators 实现。

请注意,在最终成为 JS 标准之前,可能还会发生更改。

用法示例

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    decorators: true,
  },
})

当使用 Vite 构建器或 Nitro 服务器构建时,您需要安装额外的 Babel 包作为 dev 依赖:

npm install -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
如果这些包尚未安装,Nuxt 会自动提示您进行安装。
app/app.vue
function something (_method: () => unknown) {
  return () => 'decorated'
}

class SomeClass {
  @something
  public someMethod () {
    return 'initial'
  }
}

const value = new SomeClass().someMethod()
// 这里将返回 'decorated'

Default Values

This item allows default options to be specified for core Nuxt components and composables.

These options may be moved elsewhere in the future, such as app.config or the app/ directory.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    defaults: {
      nuxtLink: {
        componentName: 'NuxtLink',
        prefetch: true,
        prefetchOn: {
          visibility: true,
        },
      },
      useAsyncData: {
        deep: true,
      },
      useState: {
        resetOnClear: true,
      },
    },
  },
})

useState.resetOnClear 选项用于控制 clearNuxtState 是否将状态重置为其初始值(由 useStateinit 函数提供),而不是将状态设置为 undefined。在 compatibilityVersion: 5 下,默认值为 true

purgeCachedData

是否在路由导航时清理 Nuxt 的静态和 asyncData 缓存。

Nuxt 将自动清除 useAsyncDatanuxtApp.static.data 的缓存数据。这有助于防止内存泄漏并确保在需要时加载最新数据,但您也可以禁用此功能。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    purgeCachedData: false,
  },
})
查看 PR #31379 以获取实现细节。

prefetchPreloadTags

<NuxtLink> 被预取且目标路由启用了 payload extraction(预渲染和缓存路由的默认设置)时,会将目标通过 useHead(或通过 @nuxt/image<NuxtImg preload> 等模块)设置的任何 <link rel="preload"> 提示转发到当前文档中。

转发的链接会从 rel="preload" 降级为 rel="prefetch",以免与当前页面的关键资源竞争。只有用户定义的 head 标签会被转发;构建时的 JS/CSS chunk preload 已由预取流水线单独处理。

此标志默认关闭,因为结合 prefetchOn: 'visibility'<NuxtLink> 的默认值)时,可能会一次触发大量跨路由预取。请在您确认这些目标预加载值得为用户通常会遇到的链接转发之后再启用它。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    prefetchPreloadTags: true,
  },
})
查看 issue #34953 了解设计动机。

prefetchPreloadTags

<NuxtLink> 被预取且目标路由启用了 payload extraction(预渲染和缓存路由的默认设置)时,会将目标通过 useHead(或通过 @nuxt/image<NuxtImg preload> 等模块)设置的任何 <link rel="preload"> 提示转发到当前文档中。

转发的链接会从 rel="preload" 降级为 rel="prefetch",以免与当前页面的关键资源竞争。只有用户定义的 head 标签会被转发;构建时的 JS/CSS chunk preload 已由预取流水线单独处理。

此标志默认关闭,因为结合 prefetchOn: 'visibility'<NuxtLink> 的默认值)时,可能会一次触发大量跨路由预取。请在您确认这些目标预加载值得为用户通常会遇到的链接转发之后再启用它。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    prefetchPreloadTags: true,
  },
})
查看 issue #34953 了解设计动机。

prefetchPreloadTags

<NuxtLink> 被预取,并且目标路由启用了 payload extraction(这是预渲染和已缓存路由的默认行为)时,会将目标通过 useHead(或通过诸如 @nuxt/image<NuxtImg preload> 之类的模块)设置的任何 <link rel="preload"> 提示转发到当前文档中。

这些被转发的链接会从 rel="preload" 降级为 rel="prefetch",这样它们就不会与当前页面的关键资源竞争。只有用户自定义的 head 标签会被转发;构建时的 JS/CSS 代码块预加载已经由预取管线单独处理。

此标志默认关闭,因为它与 prefetchOn: 'visibility'<NuxtLink> 的默认值)结合时,可能会一次性触发大量跨路由预取。等你确认目标预加载值得为用户通常会遇到的链接进行转发后,再启用它。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    prefetchPreloadTags: true,
  },
})
有关动机,请参见 issue #34953。

granularCachedData

在刷新 useAsyncDatauseFetch 的数据时(无论是由 watchrefreshNuxtData() 还是手动调用 refresh() 触发),是否调用并使用 getCachedData 的结果。

此标志默认启用,但您可以禁用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
  },
})
查看 PR #31373 以获取实现细节。

headNext

使用 head 优化:

  • 添加 capo.js head 插件,以更高效地在 head 中渲染标签。
  • 使用 hash hydration 插件来减少初始 hydration。

此标志默认启用,但你可以将其禁用:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    headNext: false,
  },
})

pendingWhenIdle

pendingWhenIdle 控制 useAsyncDatauseFetch 返回的 pending ref。

pendingWhenIdlefalse(默认值)时,pending 在请求进行中为 true,并且与 status === 'pending' 一致。当 statusidle 时,pending 始终保持 false。你可以在 { immediate: false } 的情况下看到这一点,或者在服务端渲染期间使用 { server: false } 时看到这一点。

设置 pendingWhenIdle: true,这样当 statusidle 且没有可用的缓存数据时,pending 也会为 true。当你的加载 UI 需要区分 idle 和正在进行中的请求时,请使用 status

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    pendingWhenIdle: true,
  },
})

entryImportMap

默认情况下,Nuxt 通过使用导入映射(import map)来解析 bundle 的入口 chunk,从而提高 chunk 的稳定性。

这会在 <head> 标签顶部注入一个导入映射:

<script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script>

在 Vite 发出的脚本 chunk 中,导入将来自 #entry。这意味着入口的更改不会使那些未改变的 chunk 失效。

如果您已将 vite.build.target 配置为包含不支持 import maps 的浏览器,或者已将 vite.build.rolldownOptions.output.entryFileNames 配置为不包含 [hash] 的值,Nuxt 会智能地禁用此功能。

如果需要禁用该功能,您可以这样做:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    entryImportMap: false,
  },
  // 或者,更好地告诉 vite 您的期望目标
  // Nuxt 将尊重此配置
  vite: {
    build: {
      target: 'safari13',
    },
  },
})

typescriptPlugin

启用 @dxup/nuxt 模块的增强 TypeScript 开发体验。

这个实验性插件为在 Nuxt 应用中使用 TypeScript 提供了更好的集成和开发工具,以改善开发体验。

此标志默认情况下是禁用的,但您可以启用此功能:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    typescriptPlugin: true,
  },
})
要使用此功能,您需要:
  • typescript 安装为依赖项
  • 配置 VS Code 以使用您的工作区 TypeScript 版本(请参阅 VS Code 文档
了解更多关于 @dxup/nuxt 的信息。

ssrStreaming

启用 SSR 流式传输,以显著提升首字节时间(TTFB)。启用后,服务器会立即发送 HTML 外壳(包括 <head>、样式、预加载提示和入口脚本),然后使用 Vue 的 renderToWebStream 逐步流式输出渲染后的 body 内容。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
})

对于 bot 和爬虫类 user agent(如 Googlebot、Bingbot 等),系统会自动禁用流式传输,以确保搜索引擎出于 SEO 安全获得完整渲染的 HTML。默认模式仅匹配索引爬虫;Lighthouse 和其他审计工具会刻意排除在外,因此合成测量会反映真实用户获得的同一流式响应。您可以自定义 bot 检测正则:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: {
      botRegex: /googlebot|bingbot|my-internal-crawler/i,
    },
  },
})

您也可以通过 routeRules 按路由控制流式传输:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
  routeRules: {
    '/no-stream/**': { streaming: false },
  },
})
自动回退到非流式渲染。 流式传输会在外壳被刷新时立即提交响应状态和头信息,因此与那些需要在渲染后修改响应的功能不兼容。匹配以下任一条件的请求不会采用流式传输;它们会使用缓冲式渲染器,或直接短路为重定向/错误响应:
  • routeRules 为该路由设置 noScriptscacheisrswrredirectstreaming: false
  • ssr: false 路由(已是 SPA 渲染)
  • Bot/爬虫 user agent(由 botRegex 控制)
  • 预渲染路由(nuxt generate
  • 来自插件、中间件或页面 setup 的服务端 navigateTo() 重定向
  • 初始渲染期间抛出的致命错误(在外壳刷新之前)
响应状态和头信息必须在外壳刷新之前设置。 流式传输会随着第一个字节提交 HTTP 状态和头信息,因此之后对响应所做的任何修改都无法到达客户端。这是流式传输的固有特性,不是 Nuxt 特有的 bug。边界就是外壳刷新的时刻:
  • 会到达客户端:来自 Nuxt 和 Nitro 插件的修改,这些插件会在渲染开始前运行完毕。
  • 会被丢弃:在组件渲染期间进行的 setResponseStatus()useResponseHeader()useCookie() 写入以及 h3 的 setHeader()/appendResponseHeader() 调用(包括在路由中间件或 <script setup>await 之后的调用),因为这些工作发生在外壳已经发送到网络之后。
要保留响应修改,请将其移入插件,或让该路由退出流式传输:
  • routeRules: { '/path': { streaming: false } }:静态、按路由配置。
  • 带有 ctx.prefersStream = falserender:route 钩子:运行时、按请求控制(例如针对会条件性设置 404 的路由)。
在开发环境中,流式处理器会记录一个警告,指出被丢弃的修改和对应路由,因此这些问题不会静默失败。
如果在流式传输过程中、HTTP 状态已提交之后发生错误,则会设置 payload.error,并且仍会输出闭合标签,形成一个结构完整的文档,这样客户端就能在 hydration 期间接收到错误并渲染错误页。在外壳刷新之前抛出的错误会进入缓冲式错误渲染器,并带有正确的状态码。
路由样式会流式传输,JS 提示仅针对入口。 外壳会在路由渲染之前刷新,因此其 <head> 只包含入口 chunk 的样式和提示。一旦渲染注册了页面和布局模块,它们的 CSS 就会紧跟在外壳之后流式输出(在启用 inlineStyles 时以内联 <style> 形式输出,否则以样式表链接形式输出),因此页面、布局以及顶层异步组件的样式会在 body 绘制前到达(嵌套异步组件存在 FOUC 注意事项,见下文)。特定路由的 JS chunk 不会从外壳中预加载;浏览器会在解析入口脚本后发现它们。流式传输会为每条路由提升 TTFB;对于 JS 与入口 chunk 重叠较多的路由,LCP 收益最大。
组件岛与流式传输兼容。 岛内插槽内容和选择性客户端(nuxt-client)组件通常会在渲染后阶段被拼接进 HTML,但一旦 body 已经流经岛锚点,这就不可能了。为此,渲染器会将每个岛的 teleport 以无副作用的 <template> 形式输出到文档末尾,并使用一个在 hydration 之前运行的内联脚本将其移动到位。对应用代码而言这是透明的。例外是使用 features.noScripts 构建且包含岛组件的应用,因为此时 relocation 脚本无法运行,所以会回退到缓冲式渲染器。

模块钩子

模块通过现有的 render:html 钩子参与流式响应(现在第二个参数上带有 streaming: true 标志),并额外提供一个按请求决策钩子和两个仅用于流式传输的钩子:

  • render:route 会在每次渲染开始前、每个请求触发一次(无论是否启用流式传输)。读取 ctx.canStream 可查看该路由是否可流式传输,并设置 ctx.prefersStream = false 以强制本次请求使用缓冲式渲染,例如基于 cookie、认证状态或 A/B 分桶。渲染器仅在 canStream && prefersStream 为真时才会流式传输。这是静态 routeRules / botRegex 配置的运行时逃生口。
  • render:html 会在外壳刷新之前触发一次,其第二个参数上带有 streaming: true。对 htmlAttrsheadbodyAttrsbodyPrepend 的修改会真正发送到网络。对 body/bodyAppend 的修改会被丢弃,因为 body 即将开始流式输出(开发模式下会发出警告)。仅修改 head 字段的模块(CSP 注入、OG 标签、分析元数据)无需改动代码即可在流式传输中正常工作。
  • render:html:chunk 会在渲染器生成每个 chunk、并将其入队之前触发。修改 ctx.chunk: Uint8Array 可转换字节流(例如注入 nonce);读取 ctx.index 可区分第一个 chunk 与后续 chunk。
  • render:html:close 会在 body 流完成后、闭合标签之前触发。修改 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 }
    // 对流式(pre-shell)和缓冲式(post-render)路径都有效。
    for (let i = 0; i < ctx.head.length; i++) {
      ctx.head[i] = ctx.head[i].replace(/<script(?![^>]*\snonce=)/g, `<script nonce="${nonce}"`)
    }
  })
})
CSP nonce。 流式渲染器会输出若干绕过 unhead 的内联脚本和样式:bootstrap 队列、IIFE、suspense head 推送、岛 teleport 重定位以及路由 <style> 块。如果渲染后的 head 脚本上存在 nonce,渲染器会自动在所有这些内容上复用它,因此严格的 script-src/style-src 'nonce-…' 策略不会阻止流式传输。模块只需把 nonce 放到 head 脚本上(如上所示);render:html:chunk 钩子仍可用于为组件渲染到 body 中的脚本打标。
SFC 样式在开发模式下的 FOUC: 在开发环境中,Vite 会将 SFC <style> 块作为 JavaScript 模块提供,并在模块执行后由客户端注入样式,而不是在外壳中输出对应的 <link>。使用流式传输时,浏览器会在这些样式注入模块运行之前开始绘制流式 DOM,因此 SFC 定义的样式会短暂以未应用样式的形式闪烁。解决方法:将影响首屏绘制的关键样式放入通过 css: ['~/assets/main.css'] 注册的全局 CSS 文件中。全局 CSS 文件会作为 <link rel="stylesheet"> 输出到外壳的 <head> 中,并在 body 内容流式输出之前生效。SFC <style> 块仍然适合用于不影响初始绘制的组件作用域样式。生产构建会将所有样式提取为真实的 CSS 文件(或通过 features.inlineStyles 内联),因此这只影响 nuxt dev。请使用 nuxt build && nuxt preview 验证流式渲染效果。
嵌套异步组件在生产环境中的 FOUC: 渲染器会将路由 CSS 内联到紧跟外壳之后发送的一个 chunk 中。它只能内联在那一刻已经注册的组件样式:页面、布局,以及直接放在 <Suspense> 边界内的任何异步组件(Vue 会在渲染开始时主动实例化这些组件)。一个被渲染在另一个异步组件内部的异步组件,只有在其父组件解析完成后才会被实例化,也就是在第一个 chunk 已经流式输出之后。它的 SFC <style> 会错过外壳后的样式 chunk,并在最终的 HTML 闭合部分输出,位于该组件自己的 DOM 之后。浏览器会在最后一个 chunk 到达之前将该组件绘制为未应用样式状态。可通过避免在深层嵌套异步组件中放置影响首屏绘制的样式来规避:
  • 将控制初始绘制的样式放入全局 CSS 文件(css: ['~/assets/main.css']);这些样式会进入外壳 <head>
  • 使用工具类(Tailwind、UnoCSS)进行样式化:工具类 CSS 位于入口样式表中,而不是按组件的 <style> 块中。
  • 将拥有影响首屏绘制样式的异步组件直接放在 <Suspense> 边界下,而不是嵌套在另一个异步父组件之后。
  • 或者通过 routeRules: { '/path': { streaming: false } } 让该路由退出流式传输。
对于深层嵌套异步组件中不影响首屏绘制的作用域样式而言,这没有问题:短暂闪烁只会影响首屏以上的内容。

ssrStreaming

启用 SSR 流式传输,可显著提升首字节时间(TTFB)。启用后,服务器会立即发送 HTML 外壳(包括 <head>、样式、预加载提示和入口脚本),然后使用 Vue 的 renderToWebStream 逐步流式传输渲染后的主体内容。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
})

对于 bot 和爬虫用户代理(例如 Googlebot、Bingbot 等),流式传输会自动禁用,以确保搜索引擎接收到完整渲染的 HTML,保证 SEO 安全。默认模式只匹配索引爬虫;Lighthouse 和其他审计工具会刻意排除在外,因此合成测量反映的是用户实际获得的同样流式响应。你可以自定义 bot 检测的正则表达式:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: {
      botRegex: /googlebot|bingbot|my-internal-crawler/i,
    },
  },
})

你也可以通过 routeRules 按路由控制流式传输:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
  routeRules: {
    '/no-stream/**': { streaming: false },
  },
})
自动回退到非流式渲染。 流式传输会在外壳刷新后立即提交响应状态和响应头,这与那些需要在渲染后修改响应的功能不兼容。匹配以下任意条件的请求不会进行流式传输;它们会使用缓冲渲染器,或者直接短路为重定向或错误响应:
  • 该路由的 routeRules 设置了 noScriptscacheisrswrredirectstreaming: false
  • ssr: false 路由(已是 SPA 渲染)
  • Bot/爬虫用户代理(由 botRegex 控制)
  • 预渲染路由(nuxi generate
  • 来自插件、中间件或页面 setup 中的服务端 navigateTo() 重定向
  • 初始渲染期间抛出的致命错误(在外壳刷新之前)
响应状态和响应头必须在外壳刷新之前设置。 流式传输会在发送第一个字节时提交 HTTP 状态和响应头,因此在那之后修改响应的任何内容都无法到达客户端。这是流式传输本身的特性,不是 Nuxt 特有的 bug。边界就是外壳刷新:
  • 会到达客户端:来自 Nuxt 和 Nitro 插件的修改,它们会在渲染开始前运行完毕。
  • 会被丢弃:在组件渲染期间(包括在路由中间件或 <script setup>await 之后)进行的 setResponseStatus()useResponseHeader()useCookie() 写入,以及 h3 的 setHeader()/appendResponseHeader() 调用,因为这些工作发生在外壳已经上网之后。
如果想保留某个响应修改,把它移到插件里,或者让该路由退出流式传输:
  • routeRules: { '/path': { streaming: false } }:静态配置,按路由生效。
  • 带有 ctx.prefersStream = falserender:route 钩子:运行时配置,按请求生效(例如某些会条件性设置 404 的路由)。
在开发环境中,流式处理器会记录一条警告,指出被丢弃的修改和对应路由,因此这些问题不会静默失败。
如果在流式传输过程中、HTTP 状态已经提交后发生错误,payload.error 会被设置,并且仍会输出闭合标签作为一个格式正确的文档,这样客户端会在 hydration 期间接收到错误并渲染错误页面。在外壳刷新之前抛出的错误会落入带有正确状态码的缓冲错误渲染器。
路由样式会流式传输,JS 提示仅限入口。 外壳会在路由渲染之前刷新,因此其 <head> 只包含入口 chunk 的样式和提示。一旦 render 注册了页面和布局模块,它们的 CSS 会紧接着外壳之后流式传输(在启用 inlineStyles 时以内联 <style> 形式,否则作为样式表链接),因此页面、布局以及顶层异步组件的样式会在 body 绘制前到达(嵌套异步组件会有 FOUC 注意事项,见下文)。特定路由的 JS chunk 不会从外壳预加载;浏览器会在解析入口脚本后发现它们。流式传输可提升所有路由的 TTFB;对于其 JS 与入口 chunk 重叠的路由,LCP 收益最大。
组件岛与流式传输兼容。 岛的插槽内容和选择性客户端(nuxt-client)组件通常会在渲染后的后处理阶段拼接进 HTML,而一旦 body 已经流过岛锚点,这在流式传输下就不可能了。为此,渲染器会将每个岛的 teleport 作为无害的 <template> 放在文档末尾,并通过一个在 hydration 前运行的内联脚本把它们搬回原位。这个过程对应用代码是透明的。例外情况是使用 features.noScripts 且包含岛组件的应用,由于无法运行搬运脚本,它们会回退到缓冲渲染器。

模块钩子

模块通过现有的 render:html 钩子参与流式响应(现在第二个参数上带有 streaming: true 标记),再加上一个按请求决策的钩子和两个仅用于流式传输的钩子:

  • render:route 在每次请求、渲染开始之前触发,对每次渲染都会执行一次(无论是否启用流式传输)。读取 ctx.canStream 可查看该路由是否可以流式传输,并设置 ctx.prefersStream = false 来强制该请求使用缓冲渲染,例如基于 cookie、认证状态或 A/B 分流。只有在 canStream && prefersStream 时渲染器才会流式传输。这是针对静态 routeRules / botRegex 配置的运行时逃生通道。
  • render:html 在外壳刷新之前触发一次,第二个参数上的 streaming: true 表示当前为流式模式。对 htmlAttrsheadbodyAttrsbodyPrepend 的修改会到达网络。对 body/bodyAppend 的修改会被丢弃,因为 body 即将开始流式传输(会在开发模式下发出警告)。只修改 head 字段的模块(CSP 注入、OG 标签、分析元数据)无需改动代码即可在流式传输中正常工作。
  • render:html:chunk 在渲染器生成每个 chunk 并将其入队之前触发。修改 ctx.chunk: Uint8Array 可转换字节流(例如注入 nonce);读取 ctx.index 可区分第一个 chunk 与后续 chunk。
  • render:html:close 在 body 流结束后、闭合标签之前触发。修改 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 }
    // 对流式(pre-shell)和缓冲(post-render)路径都有效。
    for (let i = 0; i < ctx.head.length; i++) {
      ctx.head[i] = ctx.head[i].replace(/<script(?![^>]*\snonce=)/g, `<script nonce="${nonce}"`)
    }
  })
})
CSP nonce。 流式渲染器会输出多个绕过 unhead 的内联脚本和样式:引导队列、IIFE、suspense head 推送、island-teleport 重新定位,以及路由 <style> 块。如果渲染后的 head 脚本上存在 nonce,渲染器会自动将其复用于所有这些内容,因此严格的 script-src/style-src 'nonce-…' 策略不会阻止流式传输。模块只需要把 nonce 放到 head 脚本上(如上所示);render:html:chunk 钩子仍可用于为组件渲染到 body 中的脚本加标记。
SFC 样式在开发模式下会出现 FOUC: 在开发环境中,Vite 会将 SFC <style> 块作为 JavaScript 模块提供,这些模块在执行后才会在客户端注入样式,而外壳中并没有对应的 <link>。在流式传输下,浏览器会在这些样式注入模块运行之前就开始绘制流式 DOM,因此 SFC 定义的样式会短暂地以无样式状态闪现。解决方法:把对首屏绘制至关重要的样式放到通过 css: ['~/assets/main.css'] 注册的全局 CSS 文件中。全局 CSS 文件会以 <link rel="stylesheet"> 的形式出现在外壳的 <head> 中,并在 body 内容开始流式传输之前生效。对于不影响首屏绘制的组件级样式,SFC <style> 块仍然完全可用。生产构建会把所有样式提取为真正的 CSS 文件(或者通过 features.inlineStyles 内联),因此这只会影响 nuxt dev。请使用 nuxt build && nuxt preview 来验证流式传输下的视觉效果。
嵌套异步组件在生产环境下会出现 FOUC: 渲染器会将路由 CSS 内联到一个在外壳之后立刻发送的 chunk 中。它只能内联当时已经注册的组件的样式:页面、布局,以及直接放在 <Suspense> 边界内的任何异步组件(Vue 会在渲染开始时急切实例化这些组件)。一个在另一个异步组件内部渲染的异步组件,只有在其父组件解析完成后才会实例化,也就是第一个 chunk 已经流式传输之后。它的 SFC <style> 会错过外壳之后的样式 chunk,而是在闭合 HTML 中输出,位于组件自身 DOM 之后。浏览器会在最终 chunk 到达之前将该组件绘制为无样式状态。避免这种情况的方法是,不要让深层嵌套的异步组件承担首屏绘制所需的样式:
  • 将影响首屏绘制的样式放到全局 CSS 文件中(css: ['~/assets/main.css']);这些样式会到达外壳 <head>
  • 使用工具类进行样式设置(Tailwind、UnoCSS):工具类 CSS 位于入口样式表中,而不是每个组件的 <style> 块里。
  • 将拥有影响首屏绘制的 <style> 的异步组件直接放在 <Suspense> 边界下,而不是嵌套在另一个异步父组件后面。
  • 或者通过 routeRules: { '/path': { streaming: false } } 让该路由退出流式传输。
嵌套异步组件上不影响首屏绘制的作用域样式是没问题的:短暂闪烁只会影响首屏以上内容。