Vercel
将 Nitro 应用部署到 Vercel。
预设: vercel
Note
与此提供商的集成支持零配置。
#入门指南
部署到 Vercel 具有以下功能以及其他特性:
在 Vercel 文档中了解更多信息。
#通过 Git 部署
Vercel 支持 Nitro 零配置部署。立即将 Nitro 部署到 Vercel。
#API 路由
Nitro 顶层的 /api 目录与 Vercel 不兼容。请改用 routes/api/ 目录。
#Bun 运行时
你可以通过在 nitro.config 中使用 vercel.functions 键指定运行时,从而使用 Bun 替代 Node.js:
import { defineConfig } from "nitro";
export default defineConfig({
vercel: {
functions: {
runtime: "bun1.x"
}
}
})或者,如果你在 vercel.json 中指定了 bunVersion 属性,Nitro 也会自动检测 Bun:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"bunVersion": "1.x"
}#每条路由的函数配置
使用 vercel.functionRules 为特定路由覆盖无服务器函数设置。每个键都是一个路由模式,其值是一个部分函数配置对象,会与基础的 vercel.functions 配置合并。
Note
路由配置中的数组属性(例如 regions)会替换基础配置中的数组,而不是与其合并。
当某些路由需要不同的资源限制、区域或功能(如 Vercel Queues 触发器)时,这非常有用。
import { defineConfig } from "nitro";
export default defineConfig({
vercel: {
functionRules: {
"/api/heavy-computation": {
maxDuration: 800,
memory: 4096,
},
"/api/regional": {
regions: ["lhr1", "cdg1"],
},
"/api/queues/process-order": {
experimentalTriggers: [{ type: "queue/v2beta", topic: "orders" }],
},
},
},
});路由模式支持通过 rou3 匹配通配符(例如,/api/slow/** 匹配 /api/slow/ 下的所有路由)。
#代理路由规则
Nitro 通过在构建时生成 CDN 级别的重写规则,自动优化 Vercel 上的 proxy 路由规则。这意味着匹配的请求将在边缘进行代理,而无需调用无服务器函数,从而降低延迟和成本。
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
// Proxied at CDN level, no function invocation
"/api/**": {
proxy: "https://api.example.com/**",
},
},
});#何时应用 CDN 重写
当以下所有条件都为真时,代理规则会被卸载到 Vercel CDN 重写:
- 目标是外部 URL(以
http://或https://开头)。 - 规则上没有设置高级
ProxyOptions。
#回退到运行时代理
当代理规则使用了以下任何 ProxyOptions 时,Nitro 会将其保留为由无服务器函数处理的运行时代理:
headers:对发往上游的请求使用自定义请求头forwardHeaders/filterHeaders:请求头过滤fetchOptions:自定义 fetch 选项cookieDomainRewrite/cookiePathRewrite:Cookie 操作onResponse:响应回调
Note
通过 headers 选项在路由规则上定义的响应头仍然会应用到 CDN 级别的重写。只有请求级别的 ProxyOptions.headers(发送到上游的)才需要运行时代理。
#定时任务(Cron 作业)
Nitro 会在构建时自动将你的 scheduledTasks 配置转换为 Vercel Cron Jobs。在 Nitro 配置中定义计划并进行部署。无需手动配置 vercel.json 中的 Cron。
import { defineConfig } from "nitro";
export default defineConfig({
experimental: {
tasks: true
},
scheduledTasks: {
// 每小时运行 `cms:update`
'0 * * * *': ['cms:update'],
// 每天午夜运行 `db:cleanup`
'0 0 * * *': ['db:cleanup']
}
})#安全化 Cron 作业端点
要防止未经授权访问 Cron 处理程序,请在 Vercel 项目设置中设置 CRON_SECRET 环境变量。设置 CRON_SECRET 后,Nitro 会验证每次 Cron 调用中的 Authorization 请求头,并对不匹配的请求返回 401。
Warning
默认情况下不会设置 CRON_SECRET。与 Vercel 自身的行为一致,当缺少此变量时,Cron 端点(/_vercel/cron,可通过 vercel.cronHandlerRoute 配置)不会执行身份验证。任何知道该路由的人都可以通过 x-vercel-cron-schedule 请求头选择一个计划,并按需运行为其注册的任务。在 Vercel 上使用 scheduledTasks 时,请始终设置 CRON_SECRET。
#队列
Nitro 集成了 Vercel Queues,可用于异步处理消息。你可以在 Nitro 配置中定义队列主题,并通过 vercel:queue 运行时钩子处理传入消息。
import { defineConfig } from "nitro";
export default defineConfig({
// 独立模式下,Nitro 会扫描 `serverDir` 中的路由和插件
serverDir: "./server",
vercel: {
queues: {
triggers: [
// 仅需 `topic`
{ topic: "notifications" },
{ topic: "orders", retryAfterSeconds: 60, initialDelaySeconds: 5 },
],
},
},
});#处理消息
在 Nitro 插件中使用 vercel:queue 钩子来处理传入的队列消息:
import { definePlugin } from "nitro";
export default definePlugin((nitroApp) => {
nitroApp.hooks.hook("vercel:queue", ({ message, metadata, send }) => {
console.log(`[${metadata.topicName}] Message ${metadata.messageId}:`, message);
});
});#从队列消息中运行任务
你可以使用队列消息来触发 Nitro 任务:
import { definePlugin } from "nitro";
import { runTask } from "nitro/task";
export default definePlugin((nitroApp) => {
nitroApp.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "orders") {
await runTask("orders:fulfill", { payload: message });
}
});
});#发送消息
直接使用 @vercel/queue 包向指定主题发送消息:
import { defineHandler } from "nitro";
import { send } from "@vercel/queue";
export default defineHandler(async (event) => {
const order = await event.req.json();
const { messageId } = await send("orders", order);
return { messageId };
});#本地开发
队列可在 nitro dev 中运行:send() 会将消息直接发送到你的 vercel:queue 钩子,因此你无需部署即可进行迭代。先使用 vercel link 和 vercel env pull 拉取你的 Vercel 环境,以便 SDK 进行身份验证。
如果你的钩子抛出异常,消息会在本地重试。设置了 retryAfterSeconds 时,重试会遵循每个触发器中的该配置。
#自定义构建输出配置
你可以在 nitro.config 中使用 vercel 键下的 vercel.config 提供额外的构建输出配置。它会与内置的自动生成配置合并。
#公共资源缓存
不会回退的公共资源目录(任何非根 baseURL 的默认行为)会由 Vercel CDN 直接从文件系统提供,并使用根据目录 maxAge 生成的 Cache-Control 请求头。没有显式设置 maxAge 的目录会缓存一年,这是 Vercel 特有的默认值,为保持向后兼容而保留。
import { defineConfig } from "nitro";
export default defineConfig({
publicAssets: [
{
baseURL: "build",
dir: "public/build",
maxAge: 3600,
},
],
})在此类基础路径下,如果请求不匹配任何文件,将返回带有 Cache-Control: no-store 的 404,而不会到达服务器函数。这与 Nitro 运行时的行为一致:在非回退基础路径下找不到资源时同样会返回 404,并可避免动态内容在资源 URL 下被提供,继而按照 max-age 的生命周期进行缓存。
设置 maxAge: 0 可选择退出一年的默认缓存时间。此时不会为该基础路径生成 Cache-Control 请求头:
export default defineConfig({
publicAssets: [
{
baseURL: "build",
dir: "public/build",
maxAge: 0,
},
],
})针对该基础路径的 cache-control 路由规则优先级高于上述两项,因此可以使用自定义请求头提供目录内容:
export default defineConfig({
routeRules: {
"/build/**": { headers: { "cache-control": "no-cache" } },
},
})Note
回退目录不受影响,包括顶层的 public/ 目录,该目录默认使用 fallthrough: true。其中缺失的文件仍会到达你的应用处理程序。
#不可变静态文件
Important
此功能目前仅在 Nitro v3 的 nightly 发布频道中可用。
客户端构建资源(例如 JS 和 CSS 分块)可以作为不可变静态文件输出。这些文件会从保留的 /_vercel/immutable/ 路径提供,因此可以跨部署共享,即使更新后的部署不再引用它们也能继续解析,从而改善跨部署缓存。
此功能需要选择启用。可以通过 vercel.immutableStaticFiles 选项启用,也可以设置 NITRO_VERCEL_IMMUTABLE_STATIC_FILES_ENABLED 环境变量。
import { defineConfig } from "nitro";
export default defineConfig({
vercel: {
immutableStaticFiles: true
}
})启用后,Nitro 会在 /_vercel/immutable/ 下输出基于内容寻址的构建资源。
Note
系统环境变量 VERCEL_HASH_SALT 会计入生成的资源路径,从而提供轮换这些路径的方式。
Warning
不可变静态文件必须从保留的 /_vercel/immutable/ 路径提供,因此在使用非根 baseURL 时不受支持。在这种情况下,Nitro 会跳过不可变输出,并在构建期间发出警告。
Note
此功能与 Nitro + Vite 集成开箱即用:Nitro 的 Vercel 预设会设置 buildAssetsDir 配置,Vite 插件会自动将其应用为客户端和服务器渲染构建的 assetsDir,因此所有生成的资源 URL 都会指向 /_vercel/immutable/ 下。
客户端构建设置和框架必须自行应用 nitro.options.buildAssetsDir。将其用作客户端打包器的资源输出目录(基础路径),以便生成的资源 URL 输出到该路径下。否则,资源仍会输出到默认位置,不可变清单将无法匹配。
#其他预设选项
Nitro 配置中的 vercel 键下提供了其他选项:
entryFormat:Vercel Functions 的处理程序格式。"web"(默认)或"node"。node格式支持与 Node.js 特定 API 的兼容性(例如req.runtime.node)。regions:边缘函数的区域列表。skewProtection:设置为false以禁用 Nitro 的 skew protection 集成(当 Vercel 控制面板中启用 skew protection 时,默认启用)。cronHandlerRoute:与scheduledTasks一起使用的 Vercel Cron 处理程序端点的路由路径(默认值:"/_vercel/cron")。
#按需增量静态再生成(ISR)
按需重新验证允许你随时清除 ISR 路由的缓存,而无需等待后台重新验证所使用的时间间隔。
Warning
不要在同一路由上同时使用 isr 和 prerender 路由规则。预渲染页面会在构建时作为静态文件写入,Vercel 会在到达 ISR 函数之前从文件系统提供这些页面,因此 isr 永远不会生效。需要在部署后重新生成时,请单独使用 isr。
要按需重新验证页面:
创建一个环境变量来存储重新验证密钥
- 你可以使用命令
openssl rand -base64 32或生成密钥来生成随机值。
更新你的配置:
import { defineConfig } from "nitro";
export default defineConfig({
vercel: {
config: {
bypassToken: process.env.VERCEL_BYPASS_TOKEN
}
}
})要按需重新验证 Prerender Function 的路径,请向该路径发起 GET 或 HEAD 请求,并在请求中添加 x-prerender-revalidate: <bypassToken> 请求头。当使用此请求头访问 Prerender Function 端点时,缓存会被重新验证,对该函数的下一次请求应返回新鲜响应。
#通过路由规则进行细粒度 ISR 配置
默认情况下,查询参数会影响缓存键,但不会传递给路由处理程序,除非另有指定。
你可以向 isr 路由规则传递选项对象来配置缓存行为:
expiration:缓存资源在通过调用无服务器函数重新生成之前的过期时间(以秒为单位)。将值设置为false(或使用isr: true路由规则)表示永不过期。group:资源的组编号。具有相同组编号的预渲染资源会同时重新验证。allowQuery:独立缓存的查询字符串参数名称列表。- 如果为空数组,则不会将查询值纳入缓存因素。
- 如果为
undefined,则每个不同的查询值都会独立缓存。 - 对于通配符
/**路由规则,始终会添加url。
passQuery:为true时,查询字符串会出现在传递给所调用函数的request参数中。allowQuery过滤器仍然适用。exposeErrBody:为true时,无论状态码为何都会公开响应正文,包括错误状态码(默认值:false)。
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
"/products/**": {
isr: {
allowQuery: ["q"],
passQuery: true,
exposeErrBody: true
},
},
},
});