Nuxt 如何用 future.compatibilityVersion 提前启用 Nuxt 5 行为并升级项目?
【免费下载链接】nuxtThe full-stack Vue framework.项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
Nuxt 5 目前仍在开发中,官方提供的做法是:在 Nuxt 4.2+ 的项目里把future.compatibilityVersion设为5,先启用 Nuxt 5 的默认行为,再按文档逐项迁移,等正式发布时项目已经就绪。本文基于 升级指南 给出这条路径的完整操作步骤:升级到最新 release、开启兼容性开关、按影响等级处理破坏性变更,最后用nuxt typecheck和开发服务器验证。
前提条件(来自 安装文档 与升级指南):
- 当前项目使用 Nuxt 4.2 或更高版本;
- Node.js 要求为22.19 或更高——Nuxt 5 要求 Node
22.19+,此时 type stripping 默认开启,nuxt.config.ts和 TypeScript 模块由运行时原生加载。
第一步:将 Nuxt 升级到最新 release
在升级compatibilityVersion之前,官方要求先把 Nuxt 升到最新版本,使用nuxt upgrade命令:
npx nuxt upgrade其他包管理器等价命令:
yarn nuxt upgradepnpm nuxt upgradebun x nuxt upgradedeno x nuxt upgrade第二步:启用 compatibilityVersion: 5
在项目的nuxt.config.ts中设置:
export default defineNuxtConfig({ future: { compatibilityVersion: 5, }, })compatibilityVersion的取值只有4 | 5(见 schema 定义)。设为5后,Nuxt 配置中的默认值会整体切换到 Nuxt v5 行为,包括:
- Vite Environment API:使用新的 Vite Environment API 改进构建配置;
- 大小写敏感路由:页面路由与 URL 大小写严格匹配,与 Nitro 一致;
- 规范化的页面组件名:页面组件名与路由名一致,
<KeepAlive>行为更一致; clearNuxtState重置为默认值:清 state 时回到初始值而不是undefined;- 非异步
callHook:callHook可能返回void而不是永远返回Promise; - 注释节点占位:client-only 组件的 SSR 占位从
<div>改为注释节点,修复 scoped styles 水合问题; - 更严格的副作用导入:生成的
tsconfig.json启用noUncheckedSideEffectImports; - Vue Options API 默认禁用:Options API 运行时从客户端包中编译掉以减小体积;
process.*类型增强移除:TypeScript 不再在NodeJS.Process上暴露已废弃的process.*标志;- Typed pages 默认开启:
experimental.typedPages默认启用,路由类型受检查; - TypeScript
baseUrl被忽略:生成的 TS 配置不再用compilerOptions.baseUrl解析 Nuxt 别名。
:: 注意:官方明确说明这一节在正式发布前仍会变化,测试 Nuxt 5 时应定期回看该文档。
按影响等级逐项迁移
文档为每项变更标注了影响等级(Minimal / Medium / Significant),并给出迁移步骤。下面是需要动手的部分。
Medium:jiti不再捆绑
Nuxt 5 不再依赖jiti,捆绑器之外加载的文件(nuxt.config.ts、modules/里的文件、layer 配置)改由 Node 运行时直接导入。TypeScript 配置仍然可以写,但有三个具体动作:
1. 相对导入加文件扩展名。nuxt.config.ts、modules/、layer 配置中的相对导入必须显式带扩展名:
- import { myPlugin } from './build/my-plugin' + import { myPlugin } from './build/my-plugin.ts'TypeScript 会把缺扩展名报为TS2835。注意它的快速修复建议写.js,但应写文件实际拥有的扩展名(.ts),由 Node 直接解析。裸包导入(如import { defu } from 'defu')不受影响。
2. 使用可擦除的 TypeScript 语法。类型注解会被擦除,但会生成运行时代码的语法不能。在配置和模块文件中替换:enum Foo {}改为const对象;namespace/module块改为普通导出;构造器参数属性(constructor(private x: string) {})改为显式赋值;移除实验性装饰器。TypeScript 会把这些统一报为TS1294。
3. 发布 layer 或模块时,交付编译后的 JavaScript。运行时会拒绝从node_modules内的任何文件剥离类型,所以发布的 TypeScript 入口无法被原生加载。发布前先构建为 JavaScript;如果包里带nuxt.config,输出为nuxt.config.mjs。项目内部的 layer 不受影响:layers/*/nuxt.config.ts可以原生加载。
4. 仍然需要jiti时安装它。jiti现在是可选 peer dependency,装上后运行时会作为兜底自动接管加载失败的文件:
npm i -D jiti(yarn / pnpm / bun 对应yarn add -D jiti、pnpm add -D jiti、bun add -D jiti。)另有一个固定依赖:nuxt.schema文件在任何 Node 版本下都需要jiti,因为其 JSDoc 注解由导入期转换读取。
Medium:迁移到 Vite Environment API
Nuxt 5 迁移到 Vite 6 的 Environment API:之前是分离的 client / server 两份 Vite 配置,现在是共享配置,插件用applyToEnvironment()指定目标环境。experimental.viteEnvironmentApi选项已被移除(Nuxt 5 中始终启用)。
关键变化:extendViteConfig()的server/client选项被废弃,使用时会有警告;用addVitePlugin()注册且只针对单环境的插件(传server: false或client: false)不会再被调用config或configResolved钩子。官方推荐改用 Vite 插件:
// Before extendViteConfig((config) => { config.optimizeDeps.include.push('my-package') }, { server: false }) // After addVitePlugin(() => ({ name: 'my-plugin', configEnvironment (name, config) { if (name === 'client') { config.optimizeDeps ||= {} config.optimizeDeps.include ||= [] config.optimizeDeps.include.push('my-package') } }, applyToEnvironment (environment) { return environment.name === 'client' }, }))已有插件的写法同样是把addVitePlugin(..., { client: false })换成插件内的applyToEnvironment钩子。
Vite 8 属于边界项:Nuxt 5 从 Vite 7 升到 Vite 8(底层打包器换为 Rolldown),但这一项不能通过future.compatibilityVersion: 5提前启用。想提前测试 Vite 8 兼容性,文档给出的方式是仅在package.json加一个 resolution override:"vite": "^8.0.0-beta.15"。若不用这个 override,Vite 8 的变化与当前任务无关。
Medium:服务器导入移到nuxt/server
Nuxt 5 提供nuxt/server导入面,覆盖defineEventHandler、createError、getQuery、readBody、cookie 与 header 辅助、sendRedirect、getRouteRules、useRuntimeConfig等最常用的服务器工具,替代已废弃的@nuxt/nitro-server/h3:
- import { defineEventHandler, getQuery } from 'nitro/h3' + import { defineEventHandler, getQuery } from 'nuxt/server'注意nuxt/server不是 h3 的完整再导出:readValidatedBody、handleCors这类辅助仍留在nitro/h3上,不需要迁移。服务器自动导入也解析到nuxt/server,所以保留自动导入的项目无需改动。
Significant:Nitro v3 相关变更
Nuxt 5 升级到 Nitro v3(基于 srvx 与 h3 v2,全面采用 Web 标准Request/ResponseAPI)。官方强调这部分仍在集成中,应预期还有进一步变化。对应用开发者最相关的迁移点:
- 服务器工具(
defineEventHandler、getQuery、readBody、useRuntimeConfig)的自动导入默认关闭。要么显式导入(优先nuxt/server),要么在迁移期间保留旧行为:
export default defineNuxtConfig({ experimental: { nitroAutoImports: true, }, })这只影响 Nitro 和 h3 提供的工具;server/utils/与shared/utils/自己的导出仍会被自动导入。
- 服务器代码中
#imports被废弃,改用#imports/server(未迁移的项目仍可运行,但 TypeScript 会报未解析)。 - 错误属性改名:
createError的statusCode/statusMessage改为status/statusText;重定向路由规则中redirect: { statusCode: 302 }改为redirect: { status: 302 }(未迁移的规则会保留原状态码并给出警告)。 useRuntimeConfig()不再接受event参数。
nitropack到nitro的包名与导入路径映射(nitropack/types→nitro/types、h3→nitro/h3等)主要影响有显式服务器导入和模块类型增强的项目,按文档中的对照表逐项替换即可。
Minimal:一组低影响变更的迁移手法
以下各项影响等级为 Minimal,但启用compatibilityVersion: 5后都会生效:
路由大小写敏感。/About不再匹配pages/about.vue。把链接改成与页面路由相同的大小写;想保留不敏感匹配:
export default defineNuxtConfig({ router: { options: { sensitive: false, }, }, })process.*检查改为import.meta.*。构建期 define 仍保留,但 TypeScript 不再承认这些属性。应用代码、模块、库里把if (process.server)换成if (import.meta.server);ESLint 规则nuxt/prefer-import-meta会标记残留用法。
callHook非异步。构建期与运行时的callHook都可能返回void,.then()/.catch()链要改成await:
- nuxtApp.callHook('my:hook', data).then(() => { ... }) + await nuxtApp.callHook('my:hook', data)想保持callHook永远返回Promise,用experimental: { asyncCallHook: true }。单项提前测试则用experimental.asyncCallHook: false。
Client-only 占位改为注释节点。.client.vue文件与createClientOnly()包装的组件在服务器端渲染<!--placeholder-->而不是空<div>。如果之前依赖占位<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>需要回退时用experimental: { clientNodePlaceholder: false }。
更严格的副作用导入。启用后无法解析的副作用导入(如import '~/assets/styles.css')在类型检查时报错(只影响nuxt typecheck和编辑器,不影响运行时)。为非代码资源加环境声明:
declare module '*.css' {}或按文档提示在typescript.tsConfig.compilerOptions中设noUncheckedSideEffectImports: false回退。
Vue Options API 默认禁用。使用export default { data() {}, methods: {}, ... }的组件(含依赖组件)需要重新开启:
export default defineNuxtConfig({ vue: { optionsApi: true, }, })defineNuxtComponent不受此标志影响。
baseUrl被忽略。从 Nuxt 的 TS 配置中移除baseUrl;如果用它锚定相对 Nuxt / Nitro 别名,把别名改为绝对路径:
+ import { fileURLToPath } from 'node:url' + export default defineNuxtConfig({ alias: { - images: './assets/images', + images: fileURLToPath(new URL('./assets/images', import.meta.url)), }, - typescript: { - tsConfig: { - compilerOptions: { - baseUrl: '..', - }, - }, - }, })Typed pages 默认开启。引用不存在的路由(如to属性拼写错误)会在类型检查时报错;需要引用运行时动态路由时扩展生成的路由类型,或用experimental: { typedPages: false }回退。$fetch/useFetch的params选项同时被移除,移到query;手工路由增强从 nitro 的InternalApi移到@nuxt/schema的ServerRoutes。
验证升级
文档给出的两条验证路径:
- 类型检查。安装依赖后运行
nuxt typecheck:
npm install --save-dev vue-tsc typescriptnpx nuxt typecheck这是判断迁移是否完成的主要信号:jiti相关变更会以TS2835(缺扩展名)和TS1294(不可擦除语法)形式在类型检查阶段暴露,副作用导入、typed pages、#imports/server未迁移等问题也会在此出现。生成环境的nodetsconfig 已被改为module/moduleResolution为nodenext并加上erasableSyntaxOnly,所以这些错误会前置到类型检查阶段(见 TypeScript 文档)。
- 开发服务器。启动开发模式确认页面在 Nuxt 5 行为下正常渲染:
npm run dev -- -o浏览器自动打开http://localhost:3000。重点核对大小写敏感路由、client-only 占位和服务器端行为是否符合预期。
限制与边界
- 该配置区在 Nuxt 5 正式发布前会持续变化,官方提示测试 Nuxt 5 的项目应定期回看升级指南。
- Nitro v3 集成仍在进行中,官方明确说应预期进一步变化;这部分变更的风险高于其余各项。
- Vite 8 不能用
future.compatibilityVersion: 5提前启用,只有前文提到的package.jsonresolution override 这一条提前测试路径,且用的是 beta 版本,不适合作为生产路径。 - 单项特性也可以不整体开启
compatibilityVersion: 5而单独提前测试,例如experimental.asyncCallHook: false、experimental.clientNodePlaceholder: true、experimental.typedPages: true;对应回退开关见上文各迁移项。 - Node 版本是硬前提:低于
22.19时,jiti移除相关变更(原生加载 TypeScript 配置)无法按文档描述工作。
【免费下载链接】nuxtThe full-stack Vue framework.项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考