Upgrade Guide
了解如何升级到最新的 Nuxt 版本。
升级 Nuxt(Upgrading Nuxt)
最新版本(Latest release)
要将 Nuxt 升级到 最新版本,请使用 nuxt upgrade 命令。
npx nuxt upgrade
yarn nuxt upgrade
pnpm nuxt upgrade
bun x nuxt upgrade
deno x 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 的行为:
export default defineNuxtConfig({
future: {
compatibilityVersion: 5,
},
})
当你将 future.compatibilityVersion 设为 5 时,你的 Nuxt 配置中各个位置的默认值都会改变,以选择加入 Nuxt v5 行为,包括:
- Vite Environment API:使用新的 Vite Environment API 以获得改进的构建配置
- 区分大小写的路由:页面路由精确匹配 URL 大小写,与 Nitro 一致
- 规范化的页面名称:页面组件名称将匹配其路由名称,以获得一致的
<KeepAlive>行为 clearNuxtState重置为默认值:clearNuxtState会将状态重置为其初始值,而不是设为undefined- 非异步的
callHook:callHook可能返回void,而不是始终返回Promise - 注释节点占位符:仅客户端组件使用注释节点而非
<div>作为 SSR 占位符,修复了一个 scoped styles 的 hydration 问题 - 更严格的副作用导入:生成的
tsconfig.json启用了noUncheckedSideEffectImports,以匹配 TypeScript 7 的默认值 - Vue Options API 默认禁用:Options API 被编译出客户端 bundle 以减小其体积
process.*类型增强已移除:TypeScript 不再在NodeJS.Process上暴露已弃用的process.*标志- 其他 Nuxt 5 改进和变更(随着可用而添加)
future.compatibilityVersion: 5 测试 Nuxt 5,请定期回来查看。破坏性或有重大影响的变更将在下方列出,并附带向前/向后兼容的迁移步骤。
process.* 类型增强已移除(Process Type Augmentation Removed)
🚦 影响级别:最小
变更内容(What Changed)
Nuxt 不再为 NodeJS.Process 增强 browser、client、dev、server 和 test。这些标志的构建时定义仍然保留以兼容,但 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)
更新链接以使用与其页面路由相同的大小写。要保留大小写不敏感匹配:
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() 方法定位特定环境的环境特定插件。
experimental.viteEnvironmentApi 选项已被移除。关键变更:
- 弃用的环境特定
extendViteConfig():extendViteConfig()中的server和client选项已被弃用,使用时会出现警告。 - 变更了插件注册:通过
addVitePlugin()注册且只针对一个环境(传入server: false或client: false)的 Vite 插件,其config或configResolved钩子不会被调用。 - 共享配置:
vite:extendConfig和vite:configResolved钩子现在使用共享配置,而不是单独的 client/server 配置。
变更原因(Reasons for Change)
Vite Environment API 提供了:
- 开发和生产构建之间更好的一致性
- 对环境特定配置更细粒度的控制
- 改进的性能与插件架构
- 支持除 client 和 server 之外的自定义环境
迁移步骤(Migration Steps)
1. 迁移为使用 Vite 插件
我们建议你使用 Vite 插件,而不是 extendViteConfig、vite:configResolved 和 vite: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: false 或 client: false 的 addVitePlugin,你可以在插件中使用新的 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 作为底层打包器。这带来了显著更快的构建,但也包含若干破坏性变更。
future.compatibilityVersion: 5 提前选择加入。如果你想提前测试 Vite 8 兼容性,可以在 package.json 中添加 "vite": "^8.0.0-beta.15" 解析覆盖。大部分迁移由 Nuxt 内部处理,但有一些需要注意的面向用户的变更:
vite.esbuild和vite.optimizeDeps.esbuildOptions已弃用,改用vite.oxc和vite.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 构建于 srvx 和 h3 v2 之上,全面采用 Web 标准 Request/Response API。这带来了性能改进和更一致的 API,但也包含对服务端代码的若干破坏性变更。
我们仍在处理 Nitro v3 的集成,所以你应该预期会有进一步的变更,以及为使迁移更直接所做的额外工作。
阅读 Nitro v3 beta 公告以了解全貌。
查看完整的 Nitro v3 迁移指南,了解所有破坏性变更。
以下小节重点介绍与 Nuxt 应用开发者和模块作者最相关的变更。
包与导入路径变更(Package and Import Path Changes)
nitropack 包已重命名为 nitro。所有导入路径都已改变:
| 之前(Before) | 之后(After) |
|---|---|
nitropack | nitro |
nitropack/types | nitro/types |
nitropack/runtime | nitro |
h3(用于服务端工具) | nitro/h3 |
服务端路由中的自动导入(defineEventHandler、getQuery、readBody、useRuntimeConfig 等)继续工作,无需更改。
如果你在服务端代码中有显式导入,请更新它们:
- 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(取代了来自 h3 的 createError):
- 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' })
})
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/app的getRouteRules():在服务端,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-ssr、estree-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
yarn add -D @vitejs/plugin-vue-jsx
pnpm add -D @vitejs/plugin-vue-jsx
bun add -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 v6,callHook 现在可能返回 void,而不是始终返回 Promise<void>。这是一个显著的性能改进,在没有任何已注册钩子或所有钩子都是同步的情况下,避免了不必要的 Promise 分配。
默认情况下(在 compatibilityVersion: 4 下),Nuxt 用 Promise.resolve() 包裹 callHook,以便现有的 .then() 和 .catch() 链式调用继续工作。在 compatibilityVersion: 5 下,移除了此包装。
变更原因(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:
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> 来继承属性(class、style 等)以实现布局目的(例如,预留空间以防止布局偏移),请将组件包装在带有 #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> 占位符行为:
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 知道该导入是有效的:
declare module '*.css' {}
nuxt.config 中禁用该选项来恢复先前的行为: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 中重新启用它:
export default defineNuxtConfig({
vue: {
optionsApi: true,
},
})
defineNuxtComponent 不受影响:它的 asyncData 和 head 选项是通过 setup() 而非 Vue Options API 处理的,因此无论此标志如何,它都能工作。