跳到主要内容

Upgrade Guide

了解如何升级到最新的 Nuxt 版本。

升级 Nuxt(Upgrading Nuxt)

最新版本(Latest release)

要将 Nuxt 升级到 最新版本,请使用 nuxt upgrade 命令。

npx nuxt upgrade

每日发布通道(Nightly Release Channel)

要在正式发布之前使用最新的 Nuxt 构建和测试特性,请阅读 nightly release channel 指南。

测试 Nuxt 5(Testing Nuxt 5)

Nuxt 5 目前正在开发中。在发布之前,可以从 Nuxt 4.2+ 版本开始测试 Nuxt 5 的许多破坏性变更。

选择加入 Nuxt 5(Opting in to Nuxt 5)

首先,将 Nuxt 升级到 最新版本

然后你可以将 future.compatibilityVersion 设为匹配 Nuxt 5 的行为:

nuxt.config.ts
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 5,
  },
})

当你将 future.compatibilityVersion 设为 5 时,你的 Nuxt 配置中各个位置的默认值都会改变,以选择加入 Nuxt v5 行为,包括:

本节在最终发布之前可能会有变化,所以如果你正在使用 future.compatibilityVersion: 5 测试 Nuxt 5,请定期回来查看。

破坏性或有重大影响的变更将在下方列出,并附带向前/向后兼容的迁移步骤。

process.* 类型增强已移除(Process Type Augmentation Removed)

🚦 影响级别:最小

变更内容(What Changed)

Nuxt 不再为 NodeJS.Process 增强 browserclientdevservertest。这些标志的构建时定义仍然保留以兼容,但 TypeScript 不会将它们视为已知属性。

请优先使用 import.meta.*。这些标志在构建时被替换,并且保持可 tree-shake。

迁移步骤(Migration Steps)

替换你的应用代码、模块和库中的遗留检查:

// 之前
// eslint-disable-next-line nuxt/prefer-import-meta
if (process.server) {
  /* ... */
}

// 之后
if (import.meta.server) {
  /* ... */
}

nuxt/prefer-import-meta ESLint 规则会标记剩余的 process.* 用法。

区分大小写的路由(Case-Sensitive Routing)

🚦 影响级别:最小

变更内容(What Changed)

compatibilityVersion: 5 下,页面路由区分大小写地匹配 URL,与 Nitro 一致。例如,/About 不再匹配 pages/about.vue

迁移步骤(Migration Steps)

更新链接以使用与其页面路由相同的大小写。要保留大小写不敏感匹配:

nuxt.config.ts
export default defineNuxtConfig({
  router: {
    options: {
      sensitive: false,
    },
  },
})

迁移到 Vite Environment API(Migration to Vite Environment API)

🚦 影响级别:中等

变更内容(What Changed)

Nuxt 5 迁移到 Vite 6 新的 Environment API,它正式化了环境的概念,并提供了对每环境配置的更好控制。

以前,Nuxt 使用单独的 client 和 server Vite 配置。现在,Nuxt 使用一个共享的 Vite 配置,配合使用 applyToEnvironment() 方法定位特定环境的环境特定插件。

Vite Environment API 在 Nuxt 5 中始终启用。experimental.viteEnvironmentApi 选项已被移除。

关键变更:

  1. 弃用的环境特定 extendViteConfig()extendViteConfig() 中的 serverclient 选项已被弃用,使用时会出现警告。
  2. 变更了插件注册:通过 addVitePlugin() 注册且只针对一个环境(传入 server: falseclient: false)的 Vite 插件,其 configconfigResolved 钩子不会被调用。
  3. 共享配置vite:extendConfigvite:configResolved 钩子现在使用共享配置,而不是单独的 client/server 配置。

变更原因(Reasons for Change)

Vite Environment API 提供了:

  • 开发和生产构建之间更好的一致性
  • 对环境特定配置更细粒度的控制
  • 改进的性能与插件架构
  • 支持除 client 和 server 之外的自定义环境

迁移步骤(Migration Steps)

1. 迁移为使用 Vite 插件

我们建议你使用 Vite 插件,而不是 extendViteConfigvite:configResolvedvite:extendConfig

// 之前
extendViteConfig((config) => {
  config.optimizeDeps.include.push('my-package')
}, { server: false })

nuxt.hook('vite:extendConfig' /* 或 vite:configResolved */, (config, { isClient }) => {
  if (isClient) {
    config.optimizeDeps.include.push('my-package')
  }
})

// 之后
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    // 你可以在这里设置全局 vite 配置
  },
  configResolved (config) {
    // 你可以在这里访问完全解析的 vite 配置
  },
  configEnvironment (name, config) {
    // 你可以在这里设置环境特定的 vite 配置
    if (name === 'client') {
      config.optimizeDeps ||= {}
      config.optimizeDeps.include ||= []
      config.optimizeDeps.include.push('my-package')
    }
  },
  applyToEnvironment (environment) {
    return environment.name === 'client'
  },
}))
2. 迁移 Vite 插件以使用环境

与其使用带有 server: falseclient: falseaddVitePlugin,你可以在插件中使用新的 applyToEnvironment 钩子。

// 之前
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    config.optimizeDeps.include.push('my-package')
  },
}), { client: false })

// 之后
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    // 你可以在这里设置全局 vite 配置
  },
  configResolved (config) {
    // 你可以在这里访问完全解析的 vite 配置
  },
  configEnvironment (name, config) {
    // 你可以在这里设置环境特定的 vite 配置
    if (name === 'client') {
      config.optimizeDeps ||= {}
      config.optimizeDeps.include ||= []
      config.optimizeDeps.include.push('my-package')
    }
  },
  applyToEnvironment (environment) {
    return environment.name === 'client'
  },
}))

了解更多关于 Vite 的 Environment API

迁移到 Vite 8(Migration to Vite 8)

🚦 影响级别:中等

变更内容(What Changed)

Nuxt 5 从 Vite 7 升级到 Vite 8,后者使用 Rolldown 取代了 esbuild 和 Rollup 作为底层打包器。这带来了显著更快的构建,但也包含若干破坏性变更。

与 Vite Environment API 迁移不同,此变更无法通过 future.compatibilityVersion: 5 提前选择加入。如果你想提前测试 Vite 8 兼容性,可以在 package.json 中添加 "vite": "^8.0.0-beta.15" 解析覆盖。

大部分迁移由 Nuxt 内部处理,但有一些需要注意的面向用户的变更:

  • vite.esbuildvite.optimizeDeps.esbuildOptions 已弃用,改用 vite.oxcvite.optimizeDeps.rolldownOptions。Vite 8 目前会自动转换这些,但将来会被移除。
  • build.rollupOptions 已弃用,改用 build.rolldownOptions
  • CommonJS 互操作行为已改变。如果你导入 CJS 模块,请查看 Vite 8 迁移指南 了解详情。

查看完整的 Vite 8 迁移指南,了解所有破坏性变更和迁移步骤。

迁移到 Nitro v3(Migration to Nitro v3)

🚦 影响级别:重大

变更内容(What Changed)

Nuxt 5 升级到 Nitro v3,这是服务端引擎的一次重大重写。Nitro v3 构建于 srvxh3 v2 之上,全面采用 Web 标准 Request/Response API。这带来了性能改进和更一致的 API,但也包含对服务端代码的若干破坏性变更。

我们仍在处理 Nitro v3 的集成,所以你应该预期会有进一步的变更,以及为使迁移更直接所做的额外工作。

阅读 Nitro v3 beta 公告以了解全貌。

查看完整的 Nitro v3 迁移指南,了解所有破坏性变更。

以下小节重点介绍与 Nuxt 应用开发者和模块作者最相关的变更。

包与导入路径变更(Package and Import Path Changes)

nitropack 包已重命名为 nitro。所有导入路径都已改变:

之前(Before)之后(After)
nitropacknitro
nitropack/typesnitro/types
nitropack/runtimenitro
h3(用于服务端工具)nitro/h3

服务端路由中的自动导入(defineEventHandlergetQueryreadBodyuseRuntimeConfig 等)继续工作,无需更改。

如果你在服务端代码中有显式导入,请更新它们:

- import { defineEventHandler, getQuery } from 'h3'
+ import { defineEventHandler, getQuery } from 'nitro/h3'

对于模块作者,类型增强必须指向新的模块路径:

- declare module 'nitropack/types' {
+ declare module 'nitro/types' {
    interface NitroRouteRules {
      myModule?: { /* ... */ }
    }
  }

错误处理:status/statusText 取代 statusCode/statusMessage

h3 v2 重命名了错误属性以与 Web 标准对齐:

  createError({
-   statusCode: 404,
-   statusMessage: 'Not Found',
+   status: 404,
+   statusText: 'Not Found',
  })

在服务端路由中,错误类现在是 HTTPError(取代了来自 h3createError):

- import { createError } from 'h3'
+ import { HTTPError } from 'nitro/h3'

  export default defineEventHandler(() => {
-   throw createError({ statusCode: 400, statusMessage: 'Bad request' })
+   throw new HTTPError({ status: 400, statusText: 'Bad request' })
  })
在你应用的 Vue 部分(即 app/ 目录),Nuxt 的 createError composable 继续工作,并且是抛出错误的推荐方式。

服务端事件 API 变更(h3 v2)

H3Event 对象现在使用 Web 标准 API:

请求属性:

- event.path              // string
+ event.url.pathname      // URL 对象 - 使用 .pathname、.search、.hash

- event.method            // string
+ event.req.method        // 通过 Web Request 对象

- event.node.req.headers  // Node.js IncomingHttpHeaders
+ event.req.headers       // Web Headers API(.get()、.set()、.has())

响应属性:

- event.node.res.statusCode = 200
+ event.res.status = 200

- event.node.res.statusMessage = 'OK'
+ event.res.statusText = 'OK'

- setResponseHeader(event, 'x-custom', 'value')
+ event.res.headers.set('x-custom', 'value')

- appendResponseHeader(event, 'set-cookie', cookie)
+ event.res.headers.append('set-cookie', cookie)

useRuntimeConfig() 不再接受 event

在 Nitro v3 中,useRuntimeConfig() 在服务端路由中不再需要(或接受)event 参数:

  export default defineEventHandler((event) => {
-   const config = useRuntimeConfig(event)
+   const config = useRuntimeConfig()
  })

路由规则:statusCode 重命名为 status

如果你定义了重定向路由规则,属性名已改变:

  export default defineNuxtConfig({
    routeRules: {
      '/old-page': {
-       redirect: { to: '/new-page', statusCode: 302 },
+       redirect: { to: '/new-page', status: 302 },
      },
    },
  })

对于模块作者:其他变更(Additional Changes)

  • Nitro 插件导入:使用 import { definePlugin } from 'nitro' 进行显式导入(自动导入仍然工作)。
  • 运行时钩子nitroApp.hooks.hook('beforeResponse', ...)nitroApp.hooks.hook('afterResponse', ...) 已被 nitroApp.hooks.hook('response', ...) 取代。
  • 来自 nitro/appgetRouteRules():在服务端,Nitro 辅助函数从 getRouteRules(event) 变为 getRouteRules(method, pathname),返回 { routeRules }

移除 experimental.externalVue

🚦 影响级别:最小

变更内容(What Changed)

experimental.externalVue 选项已被移除。当未启用 vue.runtimeCompiler 时,Vue 编译器依赖项(@babel/parser@vue/compiler-core@vue/compiler-dom@vue/compiler-ssrestree-walker)现在在服务端 bundle 中始终被替换为 mock 代理。

变更原因(Reasons for Change)

随着向 Nitro v3 迁移,所有依赖项默认会被打包进服务端输出(不像 Nitro v2 将 node_modules 外置)。externalVue 选项最初设计用于将 Vue 保留为外部依赖,这是为了避免将多个 Vue 副本打包进去所必需的,但由于 Nitro v3 无论如何都会打包所有内容,该选项变成了空操作(no-op)。

Vue 的服务端构建包含完整的编译器工具链,会将 @babel/parser(465KB)和其他编译器包不必要地拉入服务端 bundle。这些编译器包仅在启用 vue.runtimeCompiler 进行运行时模板编译时才需要。

通过始终 mock 这些编译器依赖,默认的服务端 bundle 体积减少了约 860KB(约 59%)。

迁移步骤(Migration Steps)

如果你之前显式设置了 experimental.externalVue,现在应该移除它。

  export default defineNuxtConfig({
    experimental: {
-     externalVue: false,
    },
  })
如果你使用 vue.runtimeCompiler: true,真正的编译器包仍会像以前一样被包含。

@vitejs/plugin-vue-jsx 现在变为可选(Is Now Optional)

🚦 影响级别:最小

变更内容(What Changed)

@vitejs/plugin-vue-jsx 不再默认随 @nuxt/vite-builder 安装。它现在是一个可选的 peer dependency,仅在构建过程中遇到 .jsx.tsx 文件时才按需加载。

如果你的项目使用 JSX/TSX 组件,Nuxt 会自动检测到,并提示你安装该包。

变更原因(Reasons for Change)

@vitejs/plugin-vue-jsx 插件带来了一个显著的依赖树(Babel、@vue/babel-plugin-jsx 等),对于不使用 JSX 的项目来说是不必要的。将其设为可选可以减小默认安装体积,并加快大多数 Nuxt 项目的依赖解析速度。

迁移步骤(Migration Steps)

如果你的项目使用 .jsx.tsx 文件,请将 @vitejs/plugin-vue-jsx 添加为开发依赖:

npm install -D @vitejs/plugin-vue-jsx

或者,在开发过程中首次处理 JSX/TSX 文件时,Nuxt 会提示你自动安装它。

如果你的项目不使用 JSX,则无需更改。

移除遗留的 _renderResponse 支持(Removal of Legacy _renderResponse Support)

🚦 影响级别:最小

变更内容(What Changed)

不再检查 ssrContext._renderResponse 作为回退。只有内部的 ssrContext['~renderResponse'](由 Nuxt 自己的 router composable 设置)会被使用。

变更原因(Reasons for Change)

#33896 将内部 API 迁移到 ~renderResponse 之后,ssrContext 上的 _renderResponse 属性被保留为向后兼容回退。TODO 注释表明它应在 Nuxt v5 中移除。

迁移步骤(Migration Steps)

如果你直接设置 ssrContext._renderResponse(这从来不是公开 API),请改用 ssrContext['~renderResponse']。Nuxt router composable 已经使用新属性,所以如果你通过 navigateTo 或路由中间件,则无需更改。

非异步的 callHook(Non-Async callHook

🚦 影响级别:最小

变更内容(What Changed)

随着升级到 hookable v6callHook 现在可能返回 void,而不是始终返回 Promise<void>。这是一个显著的性能改进,在没有任何已注册钩子或所有钩子都是同步的情况下,避免了不必要的 Promise 分配。

默认情况下(在 compatibilityVersion: 4 下),Nuxt 用 Promise.resolve() 包裹 callHook,以便现有的 .then().catch() 链式调用继续工作。在 compatibilityVersion: 5 下,移除了此包装。

这会影响构建时的 Nuxt 钩子(被 Nuxt 模块使用)和运行时 Nuxt 钩子(你可能会在你的应用代码中使用)。

变更原因(Reasons for Change)

Hookable v6 的 callHook 快了 20-40 倍,因为它在不需要时避免了创建 Promise。这对拥有许多钩子调用点的应用有益。

迁移步骤(Migration Steps)

如果你或你的模块使用带有 .then().catch() 链式调用的 callHook,请改用 await

- nuxtApp.callHook('my:hook', data).then(() => { ... })
+ await nuxtApp.callHook('my:hook', data)
- nuxtApp.hooks.callHook('my:hook', data).catch(err => { ... })
+ try { await nuxtApp.hooks.callHook('my:hook', data) } catch (err) { ... }
你可以通过设置 future.compatibilityVersion: 5(参见 测试 Nuxt 5)或通过使用 experimental.asyncCallHook: false 显式启用它来提前测试此特性。

或者,你可以通过如下方式确保 callHook 始终返回 Promise

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

仅客户端注释占位符(Client-Only Comment Placeholders)

🚦 影响级别:最小

变更内容(What Changed)

compatibilityVersion: 5 下,仅客户端组件(.client.vue 文件和 createClientOnly() 包装器)现在在服务端渲染一个 HTML 注释(<!--placeholder-->),而不是一个空的 <div> 元素。

变更原因(Reasons for Change)

当占位符 <div> 与实际组件根共享相同的标签名时,Vue 的运行时在 hydration 期间会跳过重新应用 setScopeId。这会导致组件挂载后缺少 scoped styles。使用注释节点完全避免了标签名冲突。

迁移步骤(Migration Steps)

如果你依赖占位符 <div> 来继承属性(classstyle 等)以实现布局目的(例如,预留空间以防止布局偏移),请将组件包装在带有 #fallback 插槽的 <ClientOnly> 中:

- <MyComponent class="placeholder" style="min-height: 200px" />
+ <ClientOnly>
+   <MyComponent />
+   <template #fallback>
+     <div class="placeholder" style="min-height: 200px"></div>
+   </template>
+ </ClientOnly>
你可以通过设置 future.compatibilityVersion: 5(参见 测试 Nuxt 5)或通过使用 experimental.clientNodePlaceholder: true 显式启用它来提前测试此特性。

或者,你可以通过如下方式恢复为先前的 <div> 占位符行为:

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

更严格的副作用导入(Stricter Side-Effect Imports)

🚦 影响级别:最小

变更内容(What Changed)

compatibilityVersion: 5 下,Nuxt 生成的 tsconfig.json 启用了 noUncheckedSideEffectImports。这是 TypeScript 7 中的一个默认值,所以提前采用可以让你的项目在该升级之前就与之保持一致。

开启此选项后,TypeScript 无法解析为模块的纯副作用导入(import './setup')现在会成为类型错误,而以前会被忽略。这仅影响类型检查(nuxt typecheck 和你的编辑器),不影响运行时行为。

变更原因(Reasons for Change)

未解析的副作用导入以前被静默忽略,因此拼写错误或已删除的文件可能通过类型检查。标记它们可以捕获这些错误,并匹配 TypeScript 7 的默认值。

迁移步骤(Migration Steps)

如果类型检查现在因对非代码资源的副作用导入(例如 import '~/assets/styles.css')而报错,请添加一个环境模块声明,让 TypeScript 知道该导入是有效的:

types.d.ts
declare module '*.css' {}
你可以通过在你的 nuxt.config 中禁用该选项来恢复先前的行为:
nuxt.config.ts
export default defineNuxtConfig({
  typescript: {
    tsConfig: {
      compilerOptions: {
        noUncheckedSideEffectImports: false,
      },
    },
  },
})

Vue Options API 默认禁用(Vue Options API Disabled by Default)

🚦 影响级别:最小

变更内容(What Changed)

compatibilityVersion: 5 下,Nuxt 将 Vue 的 __VUE_OPTIONS_API__ 特性标志设为 false,这会将 Vue 的 Options API 运行时编译出客户端 bundle。

变更原因(Reasons for Change)

Options API 运行时会随每个客户端 bundle 一起发布,即使大多数 Nuxt 应用都是使用 Composition API 和 <script setup> 编写的。移除它可减小客户端 bundle 体积(在最小应用上约为 6 kB minified / 2 kB gzipped)。

迁移步骤(Migration Steps)

如果你的任何组件(或某个依赖的组件)使用了 Options API(export default { data() {}, methods: {}, ... }),请在你的 nuxt.config 中重新启用它:

nuxt.config.ts
export default defineNuxtConfig({
  vue: {
    optionsApi: true,
  },
})
defineNuxtComponent 不受影响:它的 asyncDatahead 选项是通过 setup() 而非 Vue Options API 处理的,因此无论此标志如何,它都能工作。