Skip to content

Nuxt 4.6 发布:迈向服务器无关,重建类型化 $fetch ​

译注:本文翻译自 Nuxt 官方博客 2026 年 9 月 28 日发布的 Nuxt 4.6 版本公告,原文链接见文末。为便于中文开发者阅读,部分表述做了调整,技术细节以原文为准。

Nuxt 4.6 正式发布,并同步带来 Nuxt CLI v4。这是 Nuxt 近年来规模最大的次版本更新之一,自 v4.5.2 以来累计超过 420 个 commit,也标志着 Nuxt 向「服务器无关」(server-agnostic)迈出的第一步。

Nuxt CLI v4 ​

Nuxt CLI 发布新的主版本 @nuxt/cli v4,作为 nuxt 的依赖自动安装。新特性主要集中在 nuxt dev:

  • 终端底部新增交互面板,展示 URL、启动进度和单键快捷键(r 重启、o 打开、l 日志、n 请求、p 页面与服务器路由)。每个请求都有 id,日志和错误可归属到对应请求。若偏好传统输出,可传 --no-tui。
  • 开发服务器会说明重载或重启的原因、哪些 nuxt.config 键发生变化、慢启动的时间去向,以及各模块的初始化耗时。无实际改动的保存不再触发重启,硬重启保持同一端口。
  • 错误统一走 CLI 级通道,由 my-bad 渲染。.nuxt/ 中的锁文件允许第二个 nuxt dev(例如由 agent 启动)接管或让位,并支撑新增的 nuxt curl、nuxt task 命令,以及 nuxt docs "<query>" 文档检索。

体积与启动速度也有明显改善:@nuxt/cli 安装体积从 13.1 MB 降至 3.5 MB(-73%),依赖从 70 个降至 31 个(-56%);nuxt dev 首次绘制从 330 ms 降至 50 ms,端口绑定从 338 ms 降至 104 ms,Linux 空闲内存从 630 MB 降至 440 MB。

CLI 要求 Node.js v22.21+、v24.11+ 或 v26+;nuxt init 已被 npm create nuxt@latest 取代;不再支持 Nuxt 2 与 @nuxt/bridge。对 Nuxt v4 用户而言,这些变更不构成破坏性升级。

服务器无关的 Nuxt ​

此前 Nuxt 在客户端侧提供了充分的选择自由(是否使用 pages/、Vite/webpack/Rspack、部署平台、图片与字体服务、数据库适配器),但服务器侧始终与 h3、Nitro 的具体主版本绑定。随着 h3 与 Nitro 升级到新主版本,破坏性变更会在整个生态中层层传导。

4.6 改变了这一点:Nuxt 在 @nuxt/kit 中显式定义公共 API(不再引用外部包),自行定义请求事件、路由规则和类型化 $fetch 的类型,并新增导入入口 nuxt/server,覆盖大多数应用需要的服务器工具。底层默认仍由 Nitro 驱动,同时官方公布了实验性实现 @nuxt/vite-server,可基于 Vite Environment API 进行纯 Vite 服务器构建。

nuxt/server 的价值在于:平滑升级到 Nuxt 5(迁移至 Nitro v3 与 h3 v2)时,针对 nuxt/server 编写的服务器代码无需改动;解耦 Nuxt 与 Nitro 的发布周期;提升可维护性;保持 API 与构建器/服务器的关注点分离。

nuxt/server ​

nuxt/server 是服务器代码(handler、middleware、工具函数)的新导入源,与面向浏览器端的 nuxt/app 互补:

ts
// server/api/hello.ts
import { defineEventHandler, getQuery } from 'nuxt/server'

export default defineEventHandler((event) => {
  const { name } = getQuery<{ name?: string }>(event)
  return { message: `Hello, ${name ?? 'world'}!` }
})

工具函数基于 Web 标准,类型面向可移植的 RequestEvent:event.req(Request)、event.url(URL)、event.res(status/statusText/headers)、event.res.headers(Headers)、event.context(请求级上下文)。在 @nuxt/nitro-server 下由 Nitro 与 h3 实现,但代码不直接导入二者,因此同一 handler 可在 Nitro v2、Nitro v3 或 @nuxt/vite-server 下运行。

需要跳出 nuxt/server 时,仍可从 h3 或 nitropack/runtime 导入,行为与 Nuxt 4.5 一致。若混用两种导入,会看到 NUXT_E8012 错误提示需要修改的导入。部分辅助函数行为与 h3 v1 同名函数不同:sendRedirect 返回响应而非直接发送,createError 接收 status 与 statusText,响应头通过 event.res.headers 设置。

appSecret 与 Sessions ​

Nuxt 新增根应用密钥 runtimeConfig.appSecret,通过 NUXT_APP_SECRET 设置。模块和服务器特性可用 deriveSecret(purpose) 派生各自的密钥,因此只需配置这一个密钥。开发环境下若未配置,Nuxt 会生成并持久化一个,并在首次使用派生密钥时警告;构建过程不会生成密钥。

首个使用该密钥的特性是 nuxt/server 中的会话辅助函数。会话通过 iron 密封进 cookie,无需服务端存储:

ts
// server/api/visits.ts
import { defineEventHandler, useSession } from 'nuxt/server'

export default defineEventHandler(async (event) => {
  const session = await useSession<{ visits: number }>(event)
  await session.update(data => ({ visits: (data.visits ?? 0) + 1 }))
  return { visits: session.data.visits }
})

注意:以 app 为前缀的 runtime config 键(runtimeConfig.app、runtimeConfig.appSecret)为 Nuxt 保留。

类型化 $fetch 重建 ​

类型化 $fetch 与 useFetch 长期由服务器路由推导类型,但类型来自 Nitro 的 InternalApi 接口,路由数量达到数百条后会触发 TypeScript 实例化上限(TS2589)。4.6 基于 fetchdts 重建了类型化 fetch:Nuxt 将服务器路由编译为路由树,静态路径使用精确匹配表,访问器针对具体路由集特化。解析成本现在随调用点数量而非路由数量增长。

路由数之前之后
1001,397,361 次实例化 / 0.77s51,558 / 0.26s
3005,774,425 / 3.49s(TS2589)51,558 / 0.16s
100012,831,467 / 7.44s(TS2589)51,558 / 0.20s
3000(200 调用点)71,875,148 / 53.63s(TS2589)101,678 / 0.62s

相同运行的峰值内存从 946 MB 降至 140 MB。路由集还会携带各 handler 校验的 body、query 和 headers,因此调用会据此检查:

ts
// server/api/users.post.ts 校验 { title: string, count: number }
await $fetch('/api/users', { method: 'post', body: { title: 'a', count: 1 } })
await $fetch('/api/users', { method: 'post', body: { title: 'a', count: 'no' } })
// ^ 不能赋值给 number
await $fetch('/api/users', { method: 'post' })
// ^ body 为必填

在 Nuxt 4 中该特性需通过 experimental.routeTypedFetch 开启,Nuxt 5 中默认启用。另有 experimental.strictRouteTypes 可拒绝不存在的路径调用,以及 'isomorphic' 模式将页面也类型化为 GET 路由。

开发体验与错误页 ​

开发环境下的服务端错误此前缺少源码位置和代码框,现在 SSR 堆栈会在被读取前完成映射,Youch 覆盖层替换为 my-bad,在应用自身的错误页中渲染(应用无法渲染时作为独立页面),提供映射后的堆栈和源码代码框,并带有可复制为多种格式(含可直接交给 agent 的 prompt)的「Copy error」按钮。终端中也会打印同样的报告。

配合 Nuxt CLI v4,错误还走 CLI 级单一实时通道,可跨 worker 重启存活。nuxt.config.ts 中的语法错误会作为页面提供,修复后自动重载。

此外,@HugoRCD 重绘了开发服务器启动时的加载屏,改为 WebGL2 粒子场,在 Nuxt 标识后勾勒山脉轮廓,使用单个 shader 和单次 draw call;不支持 WebGL2 时回退到静态标识,prefers-reduced-motion 下停止动画。内置 404 与错误页也换用中性配色和更轻的字重,404 页新增返回按钮。通过 experimental.prerenderErrorPages 可将错误页预渲染为真实 HTML。

Vue Vapor 支持 ​

Nuxt 现支持 Vue 3.6 的 Vapor Mode(interop 模式)。应用根节点仍使用虚拟 DOM,可通过 <script setup> 上的 vapor 属性将单个组件或页面切换为 Vapor:

vue
<!-- app/pages/index.vue -->
<script setup vapor lang="ts">
const count = ref(0)
</script>

<template>
  <button @click="count++">
    count is &#123;&#123; count &#125;&#125;
  </button>
</template>

需在 nuxt.config.ts 中设置 vue.vapor: true。路由、useAsyncData、布局和大多数内置组件无需改动即可工作。该特性要求 Vue ^3.6.0-rc.2 或更新版本,建议先在新项目中尝试。

Nuxt 5 特性提前可用 ​

Nuxt 5 的大部分新特性已在 4.6 中提供,或默认开启,或位于标志之后。future.compatibilityVersion: 5 可一次性开启 Nuxt 5 默认值,也可逐项切换。本次新增到该标志的包括:类型化页面、类型化 $fetch、大小写敏感路由、experimental.navigateToEarlyReturn、构建时提取可序列化的 definePageMeta 键、experimental.payloadExtraction: 'client'、生成的 tsconfig 中不再有 baseUrl、内联错误渲染、规范化页面名、clearNuxtState 重置为默认值、experimental.watcher: 'builder',以及不再自动导入仅服务端的 head 组合式函数。

实验性 @nuxt/vite-server ​

server.builder 自 v4.2 起可配置,本次新增第二个服务器构建器 @nuxt/vite-server,仅用 Vite 构建 Nuxt 应用:

ts
export default defineNuxtConfig({
  server: {
    builder: 'vite', // 'nitro' 为默认值
  },
})

它可产出纯客户端 SPA、带小型 Node 入口的服务端渲染应用、面向提供服务器平台的 Web 标准 fetch handler,以及通过 nuxt generate 的完全静态输出。目前它不具备 Nitro 的完整特性(无 storage、缓存、任务或服务器插件),官方预计大多数应用仍会使用 Nitro,且该 API 仍会变化。

性能 ​

在基准机器(arm64 Linux、Node.js 24.15、五次运行中位数)上,4.6 相比 4.5.2:@nuxt/kit 安装体积从 6.4 MB 降至 2.1 MB(-67%),传递依赖从 36 降至 22(-39%);入门项目 nuxt build 从 4.4 s 降至 3.6 s(-18%);200 页面/200 组件/50 路由的构建从 11.6 s 降至 10.3 s(-11%);含 300 个 <NuxtLink> 的页面 SSR 吞吐从 110 req/s 提升至 140 req/s(+26%)。

内部链接现在在服务端渲染为普通 <a>,不再使用 useLink 和响应式状态,渲染 200 个链接从 1.36 ms 降至 0.57 ms。experimental.early404 在构建时将页面路由编译为静态匹配器,无法匹配的请求跳过创建 Vue 应用与运行插件、中间件,30 页面应用的 JSON 404 从 37.1 ms 降至 0.3 ms。模板现在声明自身的失效条件,编辑组件不再重新生成全部 49 个核心模板。useCookie 每请求只解析一次 cookie 头。此外还有若干体积优化:ssr: false 页面从服务端 bundle 中 tree-shake,unctx 不再发往浏览器,@nuxt/kit 移除了 c12、untyped、confbox、pkg-types、ufo 和 mlly 等依赖。

更轻的 Payload ​

useAsyncData 和 useFetch 接受 serialize: false 以将数据排除出 payload,主要用于永不 hydrate 的组件;experimental.stripNeverHydratedData 会在 hydrate-never 组件树中自动应用。

原文链接 ​

https://nuxt.com/blog/v4-6