如果你最近在写 Nuxt 3/4、做 边缘函数(Cloudflare Workers / Vercel Edge),或者只是想要一个“像现代框架一样舒服、但又能部署到任何地方”的后端,你大概率已经在某个地方见过这个名字:Nitro。
Nuxt 官方在介绍里说得很直白:
While building Nuxt, we created a new server engine: Nitro.(在构建 Nuxt 时,我们创建了一个新的 Server Engine:Nitro。)
换句话说:Nuxt 的后端(SSR + API + 中间件 + 输出产物)很大一部分就是由 Nitro 驱动的。而更有意思的是:Nitro 并不只服务 Nuxt——它本身也可以 独立作为“Server Toolkit / Server Framework”使用。
下面我按“工程视角”把 Nitro 讲清楚:它到底解决了什么问题、核心设计是什么、怎么上手、怎么部署、以及它为什么会成为 UnJS 生态里很关键的一块拼图。
Nitro 的官网给自己的定位是 Next Generation Server Toolkit:
官网还强调了几个非常工程化的卖点:
node_modules,体积可以做到小于 1MB这些关键词拼起来,你会发现 Nitro 的心智模型更接近:
“把后端也做成可编译产物(compiled output),并通过 presets 适配不同运行时/平台。”
而 Nuxt 文档进一步补充了它在 Nuxt 世界里的意义:Nitro 会产出一个 standalone server dist,并且 Nuxt 在 nuxt build 后把它输出到 .output 目录,里面包含能在各种环境运行的 runtime 代码。
Nitro 之所以能做到“写一次,随处跑”,背后依赖的不是魔法,而是一组非常清晰的工程拆分。
Nuxt 文档明确写了:Nitro 内部使用 h3。
在 Nitro 的路由文档里,你也会看到它的 handler 本质上就是围绕一个 event(H3Event)展开:
import { defineHandler } from 'nitro'
export default defineHandler((event) => {
return 'world'
})你可以把 h3 理解成“底盘”:处理请求/响应、路由匹配、工具函数等;而 Nitro 在 h3 之上做了更上层的框架化能力(文件路由、构建产物、预设部署、存储层、插件系统等)。
Nitro 的 Deploy 文档讲得很直白:
Nitro can generate different output formats suitable for different hosting providers from the same code base.
也就是:同一份源码可以生成不同平台需要的输出格式。
它通过 built-in presets 来实现,并且支持:
NITRO_PRESET / SERVER_PRESET,或通过配置文件/CLI --preset 指定这就把“平台差异”从业务代码里剥离出来了:你不需要在业务里 if (cloudflare) ... else if (vercel) ...,而是在 构建/输出阶段 选目标。
很多 edge/serverless 平台都会更新运行时行为,导致“同一份代码,隔几个月就炸”。Nitro 在 Deploy 文档里专门提到 compatibilityDate:
官方在表格里给了具体例子,例如 Cloudflare 的 static assets 支持、Deno v2 的 Node.js compatibility 等。
Nitro 的路由系统非常像我们熟悉的现代全栈框架:
routes/ 或 api/ 目录里放文件.get.ts / .post.ts 等)文档例子(简化版)是这样的:
routes/
api/
test.ts <-- /api/test
hello.get.ts <-- /hello (GET only)
hello.post.ts <-- /hello (POST only)动态路由也采用类似 Nuxt/Next 的方括号语法:
routes/
hello/
[name].ts <-- /hello/:name对应 handler:
import { defineHandler } from 'nitro'
export default defineHandler((event) => {
const { name } = event.context.params
return `Hello ${name}!`
})Nitro 的路由文档强调了一个点:
By combining code-splitting with compiled routes, it removes the need for a runtime router.
这句话背后的工程含义很重要:
所以 Nitro 不只是“写起来像文件路由”,它的目标是把“路由/加载”也做成一个更偏编译产物的模型,从而在 serverless/edge 这种对冷启动敏感的环境里更占优势。
Nitro 支持 middleware,并且文档明确:
middleware/ 目录里自动注册此外,Nitro 的 routeRules 值得单独拎出来。
你可以对某个路径模式配置:
示例(来自文档,略作删减):
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
routeRules: {
'/blog/**': { swr: 600 },
'/old-page': { redirect: '/new-page' },
'/api/v1/**': { cors: true },
'/about': { prerender: true },
},
})如果你写过传统后端,你会发现这类能力经常散落在:
Nitro 的思路更偏“框架化”:把它们统一收到一个可版本化、可 review 的配置文件里。
Nitro 的 Storage 文档明确说明:
Nitro has built-in integration with unstorage to provide a runtime agnostic persistent layer.
它的 API 是 useStorage():
import { useStorage } from 'nitro/storage'
// 默认(根 storage)是 in-memory
await useStorage().setItem('test:foo', { hello: 'world' })
const value = await useStorage().getItem('test:foo')
// 指定 base 前缀
const testStorage = useStorage('test')
await testStorage.setItem('foo', { hello: 'world' })文档也列出了非常完整的方法表:getItem / setItem / hasItem / getKeys / mount / watch 等。
Nitro 允许你在 nitro.config.ts 里配置多个 storage mount:
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
storage: {
redis: {
driver: 'redis',
// ...redis options
},
},
})更实用的是 devStorage:
fs 或 in-memory官方文档给了典型示例(简化版):
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
storage: {
db: { driver: 'redis', host: 'prod.example.com' },
},
devStorage: {
db: { driver: 'fs', base: './.data/db' },
},
})这对 edge/serverless 场景非常关键:你不想让本地开发强依赖某个云资源,也不想为了本地跑通去写一堆 mock。
Nitro 的 Deploy 文档明确:
nitro-dev并列出了当前可自动识别的一批 provider:
文档提供了几种方式:
nitro build --preset <preset>NITRO_PRESET 或 SERVER_PRESETnitro.config.ts 里设置 preset如果你习惯把部署行为显式写在 CI 里(更可控),我个人也更推荐用 NITRO_PRESET 或 --preset。
Nitro 的 Getting Started 文档给了一个 starter template 方式:使用 giget 创建项目,并且明确支持 Node/Bun/Deno。
如果你像我一样偏好 Bun,可以直接:
bunx giget@latest nitro nitro-app --install
cd nitro-app
bun run dev然后你会得到一个跑在 http://localhost:3000/ 的 Nitro server。
构建生产产物:
bun run build预览产物:
bun run preview输出会在 .output 目录,且文档强调它几乎不依赖额外依赖,可以部署到“几乎任何 provider”。
Nuxt 的 Server Engine 文档里把 Nitro 的位置写得很清楚:
.output除此之外,Nuxt 文档还提到了一个很有工程价值的能力:Direct API Calls。
Nuxt 的 $fetch 在浏览器端会发 HTTP 请求,但在服务端会“直接调用对应函数”,从而省掉一次网络调用。
这类能力只有当 server engine 能够统一管理路由与 handler,并且在 server side 有能力做直接调用时才比较好实现——这也是 Nitro 作为 Nuxt server engine 的一个体现。
你可能会问:既然我可以用 Express/Fastify/Hono,为什么还要 Nitro?
我觉得 Nitro 的优势主要在这些场景里更明显: