Nuxt Kit 服务端扩展完全指南:使用 Nitro 工具 API 添加 Handler、插件与预渲染路由
【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
Nitro 是 Nuxt 的默认服务端引擎。本文基于 @nuxt/kit 面向模块作者暴露的 9 个 Nitro 工具函数展开,系统讲解addServerHandler、addDevServerHandler、useNitro/tryUseNitro、addServerPlugin、addPrerenderRoutes以及三组服务端自动导入 API,并深入到 nitro.ts 的底层实现与 @nuxt/nitro-server 的接入点,帮助你掌握如何在 Nuxt 模块中注册服务端路由、中间件、扩展运行时行为并管理预渲染清单。读完本文,你将具备编写可复用的服务端增强模块所需的全部 API 知识。
Nitro 与 Nuxt Kit 的关系
Nitro 是一个开源的 TypeScript 服务端框架,用于构建高性能 Web 服务器,Nuxt 将其作为服务端引擎使用。在模块开发场景中,Nuxt Kit 提供了与 Nitro 打交道的"官方通道":所有注册动作最终都落到 Nuxt 实例的配置项与钩子上。
从源码结构看,这些工具分为几类:
- 实例访问:
useNitro/tryUseNitro,获取正在运行的 Nitro 实例; - 处理器注册:
addServerHandler/addDevServerHandler,向 Nitro 增加路由与中间件; - 运行时扩展:
addServerPlugin,注入 Nitro 插件; - 预渲染管理:
addPrerenderRoutes,追加需要静态生成的动态路由; - 服务端自动导入:
addServerImports/addServerImportsDir/addServerScanDir。
它们的实现集中在 packages/kit/src/nitro.ts,类型定义则内联在 packages/kit/src/nitro-types.ts 中——之所以内联而不是直接引用nitropack/nitro,是为了让@nuxt/kit不依赖任一 Nitro 包即可编译,从而接受同一份注册结构而无论宿主 Nuxt 实际提供哪个 Nitro 主版本。
addServerHandler:注册生产环境处理器
addServerHandler用于新增一个 Nitro 服务端处理器,是模块创建自定义 API 路由或中间件的核心入口。
基础用法
import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const { resolve } = createResolver(import.meta.url) addServerHandler({ route: '/robots.txt', handler: resolve('./runtime/robots.get'), }) }, })配套的运行时处理文件(robots.get.ts中.get后缀即代表GET方法):
import { defineEventHandler } from 'nitro/h3' export default defineEventHandler(() => { return { body: `User-agent: *\nDisallow: /`, } })访问/robots.txt时返回:
User-agent: * Disallow: /函数签名与参数说明
function addServerHandler (handler: NitroEventHandler): voidhandler为处理器对象,属性如下:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
handler | string | true | 事件处理器的文件路径(相对路径会在模块内用createResolver().resolve()解析为绝对路径)。 |
route | string | false | 路径前缀或具体路由。若传入空字符串,则该处理器作为中间件使用,对所有请求生效。 |
middleware | boolean | false | 标记为中间件处理器。中间件会在每个路由前被调用,通常应不返回任何内容以便把控制权交给后续处理器。 |
lazy | boolean | false | 使用懒加载导入处理器,仅在首次被请求时才加载,适合不常访问的路由。 |
method | string | false | 路由方法匹配器。若处理文件名中已包含方法名(如robots.get),该值会被自动用作默认值。 |
从源码理解方法推断与存储位置
addServerHandler的实现揭示了两个值得注意的细节(packages/kit/src/nitro.ts#L117-L129):
- 方法从文件名推断:
normalizeHandlerMethod用正则/(\.(get|head|patch|post|put|delete|connect|options|trace)(\.\w+)*)$/从处理文件路径中提取方法并转大写。也就是说,文件命名为hello.post.ts等价于显式传入method: 'POST'。 - 入队配置而非立即生效:注册结果被
push进nuxt.options.serverHandlers数组,该数组默认值为[](见 packages/schema/src/config/nitro.ts#L65)。Nitro 真正读取这些处理器是在后续的nitro:config/nitro:init阶段。
addDevServerHandler:仅开发环境可见的处理器
addDevServerHandler添加只在开发模式下使用的服务器处理器,它不会进入生产构建产物,适合注册 Tailwind 配置查看器等仅在本地调试时需要的工具。
用法与签名
import { defineEventHandler } from 'nitro/h3' import { addDevServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addDevServerHandler({ handler: defineEventHandler(() => { return { body: `Response generated at ${new Date().toISOString()}`, } }), route: '/_handler', }) }, })函数签名:
function addDevServerHandler (handler: NitroDevEventHandler): void参数表:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
handler | EventHandler | true | 事件处理器(函数对象),开发模式下直接内联,无需文件路径。 |
route | string | false | 路径前缀或路由。传入空字符串则作为中间件。 |
实战案例:内嵌 Tailwind 配置查看器
文档给出的经典场景是给模块挂一个 Tailwind 配置查看器,注意它借助nuxt.options.app?.baseURL把路由放在应用基路径下,避免与部署前缀冲突:
import { joinURL } from 'ufo' import { addDevServerHandler, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ async setup (options, nuxt) { const route = joinURL(nuxt.options.app?.baseURL, '/_tailwind') // @ts-expect-error - tailwind-config-viewer does not have correct types const createServer = await import('tailwind-config-viewer/server/index.js').then(r => r.default || r) as any const viewerDevMiddleware = createServer({ tailwindConfigProvider: () => options, routerPrefix: route }).asMiddleware() addDevServerHandler({ route, handler: viewerDevMiddleware }) }, })在源码实现中,addDevServerHandler会把处理器追加到nuxt.options.devServerHandlers(默认[],见 packages/schema/src/config/nitro.ts#L66);到 Nitro 初始化阶段,这些开发处理器被合并进nitro.options.devHandlers(packages/nitro-server/src/index.ts#L968),因此天然与生产 handler 分流。
useNitro 与 tryUseNitro:访问 Nitro 实例
useNitro()返回当前 Nitro 实例(类型为Nitro)。注意它有两条使用约束:
警告:只能在
ready钩子触发后才能调用useNitro()。注意:对 Nitro 实例配置的修改不会生效,实例已定型,应通过配置钩子去改。
标准用法
import { defineNuxtModule, useNitro } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const resolver = createResolver(import.meta.url) nuxt.hook('ready', () => { const nitro = useNitro() // Do something with Nitro instance }) }, })function useNitro (): NitrotryUseNitro:无服务端场景的安全版本
tryUseNitro()在存在 Nitro 实例时返回它,否则返回undefined。在ready钩子运行之前没有 Nitro 实例;而当配置的server.builder不使用 Nitro 时(例如输出纯客户端 SPA 的构建器),整个生命周期内都不存在实例。凡是"没有服务器也应该正常工作"的逻辑都应优先使用它,因为此时服务端路由、路由规则和预渲染都是缺失的。
import { defineNuxtModule, tryUseNitro } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { nuxt.hook('ready', () => { const nitro = tryUseNitro() if (!nitro) { // no server: skip anything that would only run there return } }) }, })function tryUseNitro (): Nitro | undefinedserver.builder的取值解析位于 packages/schema/src/config/nitro.ts#L12-L24:字符串'nitro'与'vite'分别映射到@nuxt/nitro-server与@nuxt/vite-server这两个包名,也可传入实现了bundle方法的对象。底层实现上,useNitro()只是对tryUseNitro()判空后的包装(packages/kit/src/nitro.ts#L212-L218),而tryUseNitro()返回的是nuxt._nitro属性(packages/kit/src/nitro.ts#L228-L230)。这个属性由 @nuxt/nitro-server 在调用createNitro(nitroConfig)创建出 Nitro 实例后写入,随后触发nitro:init钩子供模块消费。
addServerPlugin:扩展 Nitro 运行时行为
addServerPlugin用于给 Nitro 添加插件,从而在请求生命周期中注入自定义逻辑(如日志、鉴权、请求改写)。
使用要点
提示:Nitro 插件机制的进一步说明可参考 Nitro 官方文档的 Plugins 章节。
警告:插件文件内必须显式从
nitro导入definePlugin,useRuntimeConfig等同理——这些不会自动注入。
用法与签名
import { addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addServerPlugin(resolve('./runtime/plugin.ts')) }, })function addServerPlugin (plugin: string): void| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
plugin | string | true | 插件文件路径。插件必须默认导出一个接收 Nitro 实例作为参数的函数。 |
模块与运行时插件示例
模块侧注册:
import { addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addServerPlugin(resolve('./runtime/plugin.ts')) }, })运行时插件监听request/response钩子:
import { definePlugin } from 'nitro' export default definePlugin((nitroApp) => { nitroApp.hooks.hook('request', (event) => { console.log('on request', event.req.url) }) nitroApp.hooks.hook('response', async (res) => { console.log('on response', await res.text()) }) })底层看,addServerPlugin会将插件路径规范化后 push 进nuxt.options.nitro.plugins(packages/kit/src/nitro.ts#L151-L179),该配置最终会通过nitro:config钩子被 Nitro 读取。
addPrerenderRoutes:补充预渲染路由
addPrerenderRoutes向 Nitro 追加需要预渲染的路由,适合让模块在静态生成阶段把那些无法从页面扫描得到的动态 URL(例如站点地图、文章详情页)写入产物。
用法与签名
import { addPrerenderRoutes, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'nuxt-sitemap', configKey: 'sitemap', }, defaults: { sitemapUrl: '/sitemap.xml', prerender: true, }, setup (options) { if (options.prerender) { addPrerenderRoutes(options.sitemapUrl) } }, })function addPrerenderRoutes (routes: string | string[]): void| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
routes | string \| string[]{lang="ts"} | true | 要预渲染的一个或一组路由。 |
实现上,addPrerenderRoutes先过滤空值,再通过nuxt.hook('prerender:routes', ...)把路由加入ctx.routesSet(packages/kit/src/nitro.ts#L184-L196)。而 Nitro 侧的prerender:routes事件会被转发为 Nuxt 钩子(packages/nitro-server/src/index.ts#L936-L938),从而形成"模块注册 → Nitro 触发 → Nuxt 收集"的闭环。
addServerImports:为服务端声明自动导入
addServerImports把指定的具名导出注册到 Nitro 的自动导入清单中,让这些函数在服务端代码里无需手动import即可使用。
用法与签名
import { addServerImports, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const names = [ 'useStoryblok', 'useStoryblokApi', 'useStoryblokBridge', 'renderRichText', 'RichTextSchema', ] names.forEach(name => addServerImports({ name, as: name, from: '@storyblok/vue' }), ) }, })function addServerImports (dirs: NuxtImport | NuxtImport[]): voidimports可以是一个或一组对象,属性如下:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | true | 待识别的导入名称。 |
from | string | true | 模块标识符,即从哪个包导入。 |
priority | number | false | 导入优先级;多个同名导入时优先使用优先级最高的。 |
disabled | boolean | false | 是否停用该导入。 |
meta | Record<string, any> | false | 导入的元数据。 |
type | boolean | false | 是否为纯类型导入。 |
typeFrom | string | false | 生成类型声明时作为from使用的值。 |
as | string | false | 导入后使用的别名。 |
客户端/服务端通用的类型注意事项
警告:若希望提供在服务端与客户端都可使用、并且能被
shared/目录消费的工具函数,则必须让addImports与addServerImports从同一个源文件导入该函数且签名完全一致。该源文件不应导入任何上下文相关的模块(如 Nitro 上下文、Nuxt App 上下文),否则类型检查阶段可能报错。
从实现看,addServerImports并不会立刻改动什么,而是注册到nitro:config钩子中,最终把导入对象 push 进config.imports.imports数组(packages/kit/src/nitro.ts#L253-L266)。
addServerImportsDir:扫描目录注册自动导入
addServerImportsDir注册一个目录,让 Nitro 扫描其中导出的函数并自动导入到服务端。
用法与签名
import { addServerImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerImportsDir(resolve('./runtime/server/composables')) }, })function addServerImportsDir (dirs: string | string[], opts: { prepend?: boolean }): void| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
dirs | string \| string[]{lang="ts"} | true | 要注册给 Nitro 扫描的一个或一组目录。 |
opts | { prepend?: boolean } | false | 若prepend为true,目录会被插入扫描列表头部(影响同名函数优先级)。 |
完整示例
模块注册目录:
import { addServerImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerImportsDir(resolve('./runtime/server/composables')) }, })被扫描目录中导出组合式函数:
export function useApiSecret () { const { apiSecret } = useRuntimeConfig() return apiSecret }随后即可在任意服务端代码中直接调用:
import { defineEventHandler } from 'nitro/h3' export default defineEventHandler(() => { const apiSecret = useApiSecret() // Do something with the apiSecret })底层实现中,注册发生在nitro:config钩子内,把目录写入config.imports.dirs;opts.prepend决定使用unshift还是push(packages/kit/src/nitro.ts#L271-L282)。
addServerScanDir:模拟~/server的完整目录语义
addServerScanDir与上面两个"只做自动导入"的 API 不同,它注册的目录会被 Nitro当作~~/server目录一样扫描其子目录,即其中的api、routes、middleware、utils子目录分别按对应语义生效。
注意:仅
~~/server/api、~~/server/routes、~~/server/middleware与~~/server/utils会被扫描。
用法与签名
import { addServerScanDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerScanDir(resolve('./runtime/server')) }, })function addServerScanDir (dirs: string | string[], opts: { prepend?: boolean }): void| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
dirs | string \| string[]{lang="ts"} | true | 要注册为 Nitro 服务端目录的一个或一组目录。 |
opts | { prepend?: boolean } | false | 若prepend为true,目录插入扫描列表开头。 |
完整示例
模块注册一个带utils子目录的服务端目录:
import { addServerScanDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerScanDir(resolve('./runtime/server')) }, })目录内提供工具函数:
export function hello () { return 'Hello from server utils!' }随后在服务端 API 中直接调用:
import { defineEventHandler } from 'nitro/h3' export default defineEventHandler(() => { return hello() // Hello from server utils! })实现上,目录被写入config.scanDirs(同样在nitro:config钩子中,见 packages/kit/src/nitro.ts#L288-L297),Nitro 会把runtime/server/api下的文件注册为API路由、runtime/server/middleware下的注册为中间件,从而实现"模块自带一套迷你server目录"的效果。
版本感知注册:面向 Nitro v2 / v3 的兼容层
本仓库的 Kit 实现比文档表格更进一步地支持了版本化注册。在 packages/kit/src/nitro-types.ts 中,NitroEventHandler区分NitroEventHandlerV2与NitroEventHandlerV3两套结构:v2 由route+ 可选method组成,v3 则把route变为必填并要求使用如"/api/:id"、"/blog/**"的 HTTP pathname 模式,还额外支持QUERY方法与format: 'web' | 'node'、env等字段。
Kit 的入口函数为同一套 API 提供了三种重载:直接传 v2 对象、用{ version: 3 }选项标记 v3 对象,或传{ 2: ..., 3: ... }的版本映射结构(见 packages/kit/src/nitro.ts#L117-L129)。resolveVersionedRegistration会根据宿主 Nitro 主版本挑选匹配变体:在 v3 宿主上,仅 v2 的注册仍会被保留并在运行时做兼容包装;而仅 v3 的注册在 v2 宿主上会被跳过,并通过诊断记录NUXT_B8024(packages/kit/src/nitro.ts#L41-L46)。这套机制让同一份模块代码可以平滑兼容新旧两代 Nitro,是文档示例之外的进阶能力。
小结与实战建议
本文围绕 docs/4.api/5.kit/12.nitro.md 拆解了 Nuxt Kit 的全部 Nitro 工具,它们都遵循"模块注册到 Nuxt 配置 →nitro:config钩子汇聚 → Nitro 引擎消费"的统一流程。实际选型时可参考以下准则:
- 路由与中间件:生产环境用
addServerHandler(推荐文件命名xxx.get.ts以省去method),仅调试用addDevServerHandler; - 运行时钩子(请求/响应处理)用
addServerPlugin,注意在插件内显式导入definePlugin与useRuntimeConfig; - 静态站点补充 URL用
addPrerenderRoutes; - 工具函数自动导入:目录整体交给
addServerImportsDir,需要同时支持shared/场景的少量函数用addServerImports,要获得完整api/routes/middleware/utils语义则用addServerScanDir; - 实例访问:优先
tryUseNitro()以兼容无服务端的 SPA 构建器,并在ready钩子之后调用;若同时面向 Nitro v2/v3 生态,善用版本映射重载。
【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考