---
title: "服务器兼容性"
description: "构建一个模块，同时支持 Nuxt 4 和 Nuxt 5，无论底层使用哪种服务器运行时。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.x/guide/modules/server-compatibility"
---
# 服务器兼容性

> 构建一个模块，同时支持 Nuxt 4 和 Nuxt 5，无论底层使用哪种服务器运行时。

Nuxt 支持多种服务器运行时。Nuxt 3 和 4 使用 Nitro v2（`nitropack`），它使用 `h3` v1；Nuxt 5 默认使用 Nitro v3（`nitro`），它使用 `h3` v2。也可以使用基于 Web API 的自定义服务器运行时，该运行时在任何环境中都兼容（`nuxt/server`）。

本指南介绍如何编写一个服务器代码可在所有这些环境中运行的模块。

::note
如果你的模块没有服务器代码，则无需关注本文内容。
::

## 推荐迁移方式

[`nuxt/server`](https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/server-imports) 是基于 Web API 编写通用服务器代码的导入入口，并随 Nuxt 4.6+ 一同发布。使用它编写的代码可运行于 `nitropack` v2、Nitro v3 以及其他服务器构建器（例如 `@nuxt/vite-server`）之下。

### 事件处理器

如果你要注册事件处理器，请保留当前的处理器（以兼容 Nuxt <4.6），并使用 `nuxt/server` 添加一个新的可移植处理器，然后同时注册两者。Nuxt 会注册应用可以运行的实现：在 Nitro v3 和其他构建器下使用可移植文件，在仍运行 `nitropack` v2 的主机上使用 v2 文件。

```ts [module.ts]
import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: { name: 'my-module' },
  setup () {
    const { resolve } = createResolver(import.meta.url)

    addServerHandler({
      route: '/api/my-module/status',
      handler: {
        nuxt: resolve('./runtime/server/status'),
        nitro2: resolve('./runtime/server/status.legacy'),
      },
    })
  },
})
```

两个文件的差异包括导入，以及处理程序中使用 h3 v1 辅助函数或 `nuxt/server` 行为不同的部分：

::code-group
```ts [runtime/server/status.ts]
import { defineEventHandler, useRuntimeConfig } from 'nuxt/server'

export default defineEventHandler(() => ({
  version: useRuntimeConfig().public.myModule.version,
}))
```

```ts [runtime/server/status.legacy.ts]
// @ts-expect-error `#imports` is typed for the app, not the server build
import { useRuntimeConfig } from '#imports'
import { defineEventHandler } from 'h3'

export default defineEventHandler(event => ({
  version: useRuntimeConfig(event).public.myModule.version,
}))
```
::

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/getting-started/upgrade#differences-from-h3-v1"}
查看 h3 v1 与 `nuxt/server` 的所有差异，以及对应的替代方案。
::

这些键指出了每个文件所依赖的服务器 API：

| 键        | 文件导入的内容                                         | 运行位置                 |
| -------- | ----------------------------------------------- | -------------------- |
| `nuxt`   | 仅 `nuxt/server`                                 | Nuxt 4.6 起支持任何服务器构建器 |
| `nitro2` | `h3`、`nitropack/runtime`、`#imports`             | `nitropack` v2       |
| `nitro3` | `nitro`、`nitro/h3`，可单独使用，也可与 `nuxt/server` 一起使用 | Nitro 服务器构建器 v3      |

Nuxt 会根据用户使用的服务器构建器，选择最合适的处理器。如果你注册的处理器无法由用户的服务器构建器运行，则会跳过该处理器并发出警告。

::tip
仅在代码需要 Nitro v3 独有的 API 时使用 `nitro3`。使用 `nuxt/server` 能让你的代码在未来保持更好的可移植性。
::

使用此模式意味着你将同时支持 Nuxt 的旧版本（<4.6）和新版本（包括 Nuxt 5）。

::important
对象形式的 `handler` 需要在模块的 `dependencies` 中声明 `@nuxt/kit@^4.6`，[模块 starter](https://nuxt.zhcndoc.com/docs/4.x/guide/modules/getting-started) 会为你完成此设置。如果 `@nuxt/kit` 只是 `devDependency` 且打包时保留为外部依赖，或被设为 `peerDependency`，则会使用应用程序的版本；低于 4.6 的版本无法识别对象形式的 `handler`。
::

要对 `nuxt` 文件进行类型检查，请将 `nuxt@>=4.6` 安装为 `devDependency`。要对 `nitro3` 文件进行类型检查，还需安装 `nitro`。

如果模块自己的别名在 Nitro 两个主版本中都能解析到正确代码，可以为两个键注册同一个文件（`{ nitro2: file, nitro3: file }`），或设置 [`meta.compatibility.server`](#%E5%A3%B0%E6%98%8E%E6%97%A0%E6%B3%95%E6%8E%A8%E6%96%AD%E7%9A%84%E5%86%85%E5%AE%B9)。

在使用 `nitropack` v2 的主机上，Nuxt 优先选择 `nitro2` 变体，因此只有在它是唯一注册项时才会运行 `nuxt` 变体。请在 Nuxt 5 上测试 `nuxt` 变体。

在 Nuxt 5 的 playground 和测试 fixture 中，h3 自动导入的 `defineEventHandler` 不可用；请从 `nuxt/server` 导入。

### 开发服务器处理程序

`addDevServerHandler` 使用相同的键，但每个变体都是模块代码中的函数，并在 Nuxt 进程中运行，而非服务器构建产物中运行。`nuxt/server` 仅供[服务器代码](https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/server-imports#server-code-only)使用，因此不要从模块代码中导入它，即使是延迟导入也不行。

请直接使用 Web `Request` 和 `Response` 编写处理程序，在 `setup` 中读取 `nuxt.options` 配置，再针对各服务器 API 适配：

```ts [module.ts]
import { addDevServerHandler, defineNuxtModule } from '@nuxt/kit'
import { defineEventHandler, toWebRequest } from 'h3'

export default defineNuxtModule({
  meta: { name: 'my-module' },
  setup (_options, nuxt) {
    const greeting = nuxt.options.runtimeConfig.public.myModule.greeting

    function greet (request: Request) {
      const name = new URL(request.url).searchParams.get('name')
      if (!name) {
        return new Response('Missing name', { status: 400 })
      }
      return Response.json({ message: `${greeting}, ${name}!` })
    }

    addDevServerHandler({
      route: '/_my-module/greet',
      handler: {
        nuxt: event => greet(event.req),
        nitro2: defineEventHandler(event => greet(toWebRequest(event))),
      },
    })
  },
})
```

`nuxt` 变体接收 `RequestEvent`，并从 `event.req` 读取请求；`nitro2` 变体会使用 `toWebRequest()` 转换 h3 v1 event。由于模块导入了 h3 v1，请在模块的 `dependencies` 中添加 `h3@^1`。

开发处理程序的路由在不同主机上的匹配方式不同。在使用 `nitropack` v2 的主机上，所有变体都像 Nitro v2 一样按路径前缀匹配，因此 `/_my-module` 也会匹配 `/_my-module/inter.woff2`。在 Nitro v3 上，`nuxt` 和 `nitro3` 变体按完整路由精确匹配。

若要让所有变体都处理路由下方的路径，请在路由末尾添加 `/**`：

```ts [module.ts]
addDevServerHandler({
  route: '/_my-module/assets/**',
  handler: {
    nuxt: event => serveAsset(event.req),
    nitro2: defineEventHandler(event => serveAsset(toWebRequest(event))),
  },
})
```

此处的 `serveAsset` 与前例中的 `greet` 一样，接收 `Request` 并返回 `Response`。该路由会匹配 `/_my-module/assets` 及其下的所有路径，因此不需要再注册基础路径。在使用 `nitropack` v2 的主机上，这需要在模块的 `dependencies` 中声明 `@nuxt/kit@^4.6.1`。

在使用 `nitropack` v2 的主机上，处理程序读取的 `event.path` 相对于其路由基路径，因为 Nitro v2 会去掉这段前缀。请求 `/_my-module/assets/inter.woff2` 时，`event.path` 是 `/inter.woff2`。完整 URL 仍可通过 `toWebRequest()` 获取，`nuxt` 变体则从 `event.req.url` 获取。

### 服务器工具函数

如果模块公开的工具函数通过别名、`addServerImports` 或类型模板接收 event，请将参数类型标注为 `nuxt/server` 中的 `RequestEvent`，或标注为其读取的字段子集，例如 `Pick<RequestEvent, 'req' | 'context'>`。类型为 `H3Event` 的参数只能接受某一个 h3 主版本的 event，因此无法从 `nuxt/server` 处理程序调用。

`getNitroVersion` 返回的是 Nitro 主版本，而不是主机运行的服务器 API；使用其他 `server.builder` 的主机不会运行 Nitro 的变体。

### 服务器插件

服务器插件也支持变体，但它们专用于 Nitro，因此没有可供注册的通用 `nuxt` 变体。

::tip
由于 `nitro` 中的 `definePlugin()` 和 `nitropack/runtime` 中的 `defineNitroPlugin()` 都是恒等函数，你可以将它们替换为类型注解，从而编写同时适用于 `nitro2` 和 `nitro3` 的代码。
::

```ts [module.ts]
import { addNitroPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: { name: 'my-module' },
  setup () {
    const { resolve } = createResolver(import.meta.url)

    addNitroPlugin({
      nitro2: resolve('./runtime/server/plugin.legacy'),
      nitro3: resolve('./runtime/server/plugin'),
    })
  },
})
```

### 其他变体

对于指向服务器代码的其他内容，例如别名、类型模板或读取服务器 event 的应用插件，可以使用 `resolveServerVariant` 解析适用于当前 Nuxt 版本的变体；如果没有适用变体，则返回 `undefined`。

`addServerImports` 的 `from` 属性也接受相同的变体形式。

```ts [module.ts]
import { addServerImports, createResolver, defineNuxtModule, resolveServerVariant } from '@nuxt/kit'

export default defineNuxtModule({
  meta: { name: 'my-module' },
  setup (_options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    const someServerFile = resolveServerVariant({
      nuxt: resolve('./runtime/server'),
      nitro2: resolve('./runtime/server.legacy'),
    })

    if (someServerFile) {
      nuxt.options.alias['#my-module/server'] = someServerFile
    }

    addServerImports({
      name: 'verifyToken',
      from: { nuxt: resolve('./runtime/verify'), nitro2: resolve('./runtime/verify.legacy') },
    })
  },
})
```

在使用 `nitropack` v2 时会优先选择 `nitro2`，使用 Nitro v3 时会优先选择 `nitro3`。因此，如果要测试 `nuxt` 变体，请在测试 fixture 中将 `server.builder` 设为非 Nitro 构建器，例如 `@nuxt/vite-server`。

### 声明无法推断的内容

Nuxt 会读取已注册文件的导入项，以判断该文件使用的 API，因此你无需声明其兼容性。但如果这还不够，你可以设置 `meta.compatibility.server`：

```ts [module.ts]
export default defineNuxtModule({
  meta: {
    name: 'my-module',
    compatibility: { server: 'nuxt' },
  },
})
```

如果我们无法判断，代码将被视为 `nitro2`。

### 类型化运行时配置和 event

要为模块添加的运行时配置键提供类型，请从 `nuxt/schema` 扩展 `RuntimeConfig`（或 `PublicRuntimeConfig`）。这些键在 `nuxt`、`nitro2` 和 `nitro3` 文件中都会有类型。

```ts [module.ts]
declare module 'nuxt/schema' {
  interface RuntimeConfig {
    myModule: { apiKey: string }
  }
}
```

构建器特定的类型（例如 `useRequestEvent()` 返回的 `H3Event`，或 `nitro:config` 钩子中的完整 Nitro 配置）来自生成的 `.nuxt/nuxt.d.ts`。没有该文件时，它们会解析为与构建器无关的类型（`RequestEvent` 和精简版 Nitro 配置）。若要支持任何服务器构建器，类型工具函数应接受 `RequestEvent`。

### 插件、存储和缓存

基于服务器插件、存储或缓存构建的模块没有 `nuxt` 变体，因为 `nuxt/server` 不涵盖这些领域。现在请将相关文件注册为 `nitro2`，移植后再添加 `nitro3` 变体。

## Nitro 专用代码

`nuxt/server` 涵盖请求和响应相关工作：处理器、错误、路由参数、查询和请求体的读取与验证、标头、CORS、Cookie、会话、重定向、路由规则、运行时配置、应用配置、通过 `serverFetch()` 进行进程内 fetch，以及通过 `useServerHooks()` 使用运行时钩子。但 Nitro 涵盖的内容远不止这些，因此如果你需要以下任一功能，可能需要在 `nitro2` 或 `nitro3` 文件中继续使用 Nitro 自身的导入项。

使用与文件对应版本的模块说明符。导入 `nitro/*` 的文件会被视为 Nitro 3 代码，因此 `nitro2` 文件必须改为导入 `h3` 和 `nitropack/*`：

| 区域                                                       | 在 `nitro2` 文件中                                 | 在 `nitro3` 文件中                     |
| -------------------------------------------------------- | ---------------------------------------------- | ---------------------------------- |
| 存储：`useStorage()` 及其驱动配置                                 | `nitropack/runtime`                            | `nitro/storage`                    |
| 缓存：`defineCachedEventHandler()`、`defineCachedFunction()` | `nitropack/runtime`                            | `nitro/cache`                      |
| 服务器插件                                                    | 来自 `nitropack/runtime` 的 `defineNitroPlugin()` | 来自 `nitro` 的 `definePlugin()`      |
| Nitro 自身的运行时钩子，例如 `request` 和 `error`                    | 来自 `nitropack/runtime` 的 `useNitroApp().hooks` | 来自 `nitro/app` 的 `useNitroHooks()` |
| 延迟处理器：`lazyEventHandler()`                               | `h3`                                           | `nitro/h3`                         |

`nuxt/server` 也不支持任务。

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/server-imports#reaching-past-the-surface"}
了解哪些 h3 辅助函数适用于可移植事件，哪些需要 Nitro 专用工具。
::

## Nitro v3 中值得注意的变更

如果你只发布 `nitro2` 文件，它仍可通过 Nitro v2 兼容层在 Nuxt 5 上运行。相关说明以及 Nitro v3 中发生变化的行为，请参阅 [Nuxt 5 模块服务器兼容性指南](https://nuxt.com/docs/5.x/guide/modules/server-compatibility)。


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
