升级指南
升级 Nuxt
最新版本
要将 Nuxt 升级到最新版本,请使用 nuxt upgrade 命令。
npx nuxt upgrade
yarn nuxt upgrade
pnpm nuxt upgrade
bun x nuxt upgrade
deno x nuxt upgrade
夜版发布渠道
要使用 Nuxt 的最新构建版本并测试新功能(尚未正式发布),请阅读夜版发布渠道指南。
测试 Nuxt 5
Nuxt 5 目前正在开发中。在发布之前,可以测试许多 Nuxt 5 从 Nuxt 版本 4.2+ 的重大更改。
选择使用 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 来改进构建配置
- Normalized Page Names:页面组件名称将匹配其路由名称,以实现一致的
<KeepAlive>行为 clearNuxtStateresets to defaults:clearNuxtState将将状态重置为其初始值,而非将其设置为undefined- Non-async
callHook:callHook可能返回void,而非始终返回Promise - Comment node placeholders:客户端专属组件使用注释节点而非
<div>作为 SSR 占位符,修复作用域样式激活问题 - 其他随着可用性发布的 Nuxt 5 改进和更改
future.compatibilityVersion: 5 测试 Nuxt 5,请定期回来查看。重大或显著的变化将在下面注明,并附上向后/向前兼容的迁移步骤。
迁移到 Vite 环境 API
🚦 影响级别:中等
变化内容
Nuxt 5 迁移到 Vite 6 的新 环境 API,该 API 正式化了环境的概念,并提供了对每个环境配置的更好控制。
之前,Nuxt 使用单独的客户端和服务器 Vite 配置。现在,Nuxt 使用共享的 Vite 配置,并配备特定于环境的插件,这些插件使用 applyToEnvironment() 方法来针对特定环境。
experimental.viteEnvironmentApi 选项已被移除。关键变化:
- 不再支持特定于环境的
extendViteConfig():extendViteConfig()中的server和client选项已被弃用,使用时将显示警告。 - 更改插件注册:使用
addVitePlugin()注册的 Vite 插件,如果仅针对一个环境(通过传递server: false或client: false)将不会调用其config或configResolved钩子。 - 共享配置:
vite:extendConfig和vite:configResolved钩子现在与共享配置一起工作,而不是单独的客户端/服务器配置。
变更原因
Vite 环境 API 提供了:
- 更好的一致性,确保开发和生产构建之间的差异最小化
- 更细粒度的环境特定配置控制
- 改进的性能和插件架构
- 支持自定义环境,而不仅仅是客户端和服务器
迁移步骤
1. 迁移到使用 Vite 插件
我们建议您使用 Vite 插件,而不是 extendViteConfig、vite:configResolved 和 vite:extendConfig。
// 之前
extendViteConfig((config) => {
config.optimizeDeps.include.push('my-package')
}, { server: false })
nuxt.hook('vite:extendConfig' /* or 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 插件以使用环境
您可以使用插件中的新 applyToEnvironment 钩子,而不是使用 addVitePlugin 结合 server: false 或 client: false。
// 之前
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 8
🚦 影响级别:中等
变化内容
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 迁移指南。
迁移到 Nitro v3
🚦 影响级别:重大
发生了什么变化
Nuxt 5 升级到了 Nitro v3,这是服务器引擎的一次重大重写。Nitro v3 基于 srvx 和 h3 v2 构建,全面采用 Web 标准的 Request/Response API。这带来了性能提升和更一致的 API,但服务器端代码存在若干破坏性变更。
以下章节重点介绍对 Nuxt 应用开发者和模块作者最相关的变化。
包及导入路径变化
nitropack 包更名为 nitro。所有导入路径都发生了变化:
| 之前 | 之后 |
|---|---|
nitropack | nitro |
nitropack/types | nitro/types |
nitropack/runtime | nitro |
h3 (for server utilities) | 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',
})
在服务器路由中,错误类由 createError 变为 HTTPError:
- 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 组合式函数依旧可用,并且推荐用于抛出错误。服务器事件 API 变化(h3 v2)
H3Event 对象现采用 Web 标准 API:
请求属性:
- event.path // 字符串
+ event.url.pathname // URL 对象 - 使用 .pathname,.search,.hash
- event.method // 字符串
+ 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 },
},
},
})
模块作者的其他变化
- Nitro 插件导入:使用
import { definePlugin } from 'nitro'进行显式导入(自动导入仍然有效)。 - 运行时钩子:
nitroApp.hooks.hook('beforeResponse', ...)和nitroApp.hooks.hook('afterResponse', ...)已被nitroApp.hooks.hook('response', ...)替代。 getRouteRules()fromnitro/app:在服务器上,Nitro 辅助函数从getRouteRules(event)更改为getRouteRules(method, pathname),返回{ routeRules }。
移除 experimental.externalVue
🚦 影响级别:极小
变化内容
experimental.externalVue 选项已被移除。当未启用 vue.runtimeCompiler 时,Vue 编译器依赖(@babel/parser、@vue/compiler-core、@vue/compiler-dom、@vue/compiler-ssr、estree-walker)现在始终在服务器包中被替换为模拟代理。
变更原因
随着迁移到 Nitro v3,所有依赖项默认都会打包到服务器输出中(不像 Nitro v2 那样将 node_modules 外部化)。externalVue 选项最初设计用于将 Vue 保持为外部依赖,这是为了避免打包多个 Vue 副本,但由于 Nitro v3 无论如何都会打包所有内容,该选项变得无效。
Vue 的服务器构建包含完整的编译器工具链,会不必要地将 @babel/parser(465KB)和其他编译器包拉入服务器包中。这些编译器包仅在启用 vue.runtimeCompiler 进行运行时模板编译时才需要。
通过始终模拟这些编译器依赖,默认服务器包大小减少了约 860KB(约 59%)。
迁移步骤
如果你之前显式设置了 experimental.externalVue,现在应该将其移除。
export default defineNuxtConfig({
experimental: {
- externalVue: false,
},
})
vue.runtimeCompiler: true,真正的编译器包仍会像以前一样包含在内。@vitejs/plugin-vue-jsx 现为可选
🚦 影响级别:极小
变化内容
@vitejs/plugin-vue-jsx 不再随 @nuxt/vite-builder 默认安装。它现在是可选的对等依赖,仅在构建过程中遇到 .jsx 或 .tsx 文件时按需加载。
如果你的项目使用 JSX/TSX 组件,Nuxt 将自动检测并提示你安装该包。
变更原因
@vitejs/plugin-vue-jsx 插件会引入大量依赖树(Babel、@vue/babel-plugin-jsx 等),对于不使用 JSX 的项目来说是不必要的。将其设为可选可减少默认安装大小并加快大多数 Nuxt 项目的依赖解析速度。
迁移步骤
如果你的项目使用 .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
或者,Nuxt 将在开发期间首次处理 JSX/TSX 文件时自动提示你安装。
如果你的项目不使用 JSX,则无需更改。
移除旧版 _renderResponse 支持
🚦 影响级别:极小
发生了什么变化
不再支持对 ssrContext._renderResponse 的兼容回退检测。现在仅使用 Nuxt 导航组合函数设置的内部 ssrContext['~renderResponse']。
变更原因
ssrContext 上的 _renderResponse 属性在内部 API 迁移到 ~renderResponse(见 #33896)后,仅作为兼容回退保留。TODO 注释指出应在 Nuxt v5 中移除。
迁移步骤
如果你之前直接设置过 ssrContext._renderResponse(这从未是公开 API),请改用 ssrContext['~renderResponse']。Nuxt 导航组合函数已使用新属性,因此如果你使用 navigateTo 或路由中间件,则无需更改。
非异步 callHook
🚦 影响级别:极小
变化内容
升级到 hookable v6 后,callHook 现在可能返回 void,而非一直返回 Promise<void>。这是一项性能改进,可在没有钩子或仅有同步钩子的情况下避免不必要的 Promise 分配。
默认情况下(兼容版本为 4),Nuxt 使用 Promise.resolve() 包装 callHook,以确保现有的 .then() 和 .catch() 链式调用正常工作。设置兼容版本为 5 时,将移除此包装。
变更原因
Hookable v6 避免不必要的 Promise 创建,使钩子调用速度提高 20–40 倍,利于有大量钩子的应用。
迁移步骤
如果你(或你使用的模块)在 callHook 上使用 .then() 或 .catch(),请改用 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,
},
})
客户端专属注释占位符
🚦 影响级别:极小
变化内容
使用 compatibilityVersion: 5 时,客户端专属组件(.client.vue 文件和 createClientOnly() 包装器)现在在服务器上渲染 HTML 注释(<!--placeholder-->)而非空 <div> 元素。
- 在
nuxt.config.ts的modules数组中定义的模块 modules/目录中自动发现的模块
当占位符 <div> 与实际组件根元素共享相同标签名时,Vue 运行时在激活期间会跳过重新应用 setScopeId。这会导致组件挂载后作用域样式丢失。使用注释节点可完全避免标签名冲突。
此改动确保:
如果你依赖占位符 <div> 来继承属性(class、style 等)以用于布局(例如,预留空间防止布局偏移),请将组件包装在 <ClientOnly> 中并添加 #fallback 插槽:
- <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,
},
})