Vercel 部署 Nuxt.js (Vue) 实战:SSR / SSG / Nitro 全栈上云指南

详细讲解如何在 Vercel 上部署 Nuxt.js + Vue 3 项目:SSR 与 SSG 模式选择、Nitro Server 路由配置、环境变量、Vercel Functions 适配、图片与缓存优化。附完整代码和部署验证命令。

Nuxt.js(Vue 生态的全栈框架)在 Vercel 上有着极佳的支持:通过内置的 Nitro Server 引擎,Nuxt 项目可以完美适配 Vercel Functions 的运行时,实现 SSR(服务端渲染)、SSG(静态生成)、API Routes、Server Middleware 等所有能力。本文从初始化项目到生产部署,逐个讲解 Nuxt 在 Vercel 上的最佳实践。


一、项目初始化与模式选择

1.1 创建 Nuxt 项目

# 创建项目
npx nuxi@latest init my-nuxt-app

# 进入目录并安装依赖
cd my-nuxt-app
npm install

# 安装 Vercel 适配器(必须)
npm install -D @nuxtjs/vercel-builder
# 实际 Nuxt Nitro 自带 vercel 预设,通常不需要额外安装

Nuxt 3 的 Nitro 引擎自带多种 preset,其中 vercel 是专门为 Vercel Functions 优化过的。

1.2 SSR vs SSG vs ISR:怎么选?

Nuxt 的渲染模式由 nuxt.config.ts 中的 ssrrouteRules 决定:

模式配置适用场景Vercel 适配
SSRssr: true(默认)需要服务端数据的动态页、登录态页面✅ Nitro → Vercel Serverless Functions
SSGssr: true + routeRules: { '/**': { isr: false, prerender: true } }静态站、博客、文档✅ 静态文件直接上传到 Vercel CDN
ISRrouteRules: { '/**': { isr: 60 } }大部分内容不常变但需及时更新✅ 混合:静态 CDN + Serverless stale-while-revalidate
SPAssr: false纯后台管理、强客户端交互应用✅ 仅 HTML + JS 文件

建议决策

  • 内容站/博客:SSG(最快、最省 Serverless 调用)
  • SaaS 前台:ISR(兼顾速度和数据新鲜度)
  • 后台管理系统:SSR 或 SPA

1.3 nuxt.config.ts 核心配置

// nuxt.config.ts
export default defineNuxtConfig({
  // 默认开启 SSR
  ssr: true,

  // 静态资源/缓存策略
  routeRules: {
    // 首页 SSR(动态内容)
    '/': { isr: 60 },
    // 博客列表页:ISR 5 分钟刷新一次
    '/blog/**': { isr: 300 },
    // 博客详情页:静态生成 + 按需重新验证
    '/blog/**': { prerender: true, isr: 3600 },
    // 关于页:纯静态,永不重新验证
    '/about': { prerender: true },
    // API 路由:不走缓存
    '/api/**': { cors: true, isr: false },
  },

  nitro: {
    // 显式指定 Vercel 预设
    preset: 'vercel',
    // 为 Vercel Functions 设置内存和超时
    vercel: {
      functions: {
        maxDuration: 10, // 函数最大执行时间(秒)
      },
    },
  },

  // 开发服务器代理(本地开发用)
  devtools: { enabled: true },
  compatibilityDate: '2025-01-01',
});

*preset: 'vercel'* 是核心配置,它告诉 Nitro 将服务端代码打包为 Vercel Functions 兼容的格式。


二、API Routes 与 Server 引擎

Nuxt 3 的 Server 引擎基于 Nitro,API 路由放在 server/api/server/routes/ 下,Vercel 会自动将其转换为 Serverless Functions。

2.1 基础 API 路由

// server/api/hello.get.ts
export default defineEventHandler((event) => {
  return {
    message: 'Hello from Nuxt on Vercel!',
    timestamp: new Date().toISOString(),
  };
});

文件命名约定:

  • .get.ts → 只响应 GET 请求
  • .post.ts → 只响应 POST 请求
  • .ts → 响应所有 HTTP 方法(需手动判断 event.method

2.2 带参数的 API

// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id');

  // 这里可以查询数据库
  // const user = await db.findUserById(id);

  return {
    id,
    name: `User ${id}`,
  };
});

2.3 与数据库交互

以 Prisma + Postgres 为例:

// server/utils/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as { prisma?: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient();

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
// server/api/posts/index.get.ts
import { prisma } from '~/server/utils/prisma';

export default defineEventHandler(async () => {
  const posts = await prisma.post.findMany({
    orderBy: { createdAt: 'desc' },
    take: 20,
  });
  return posts;
});

⚠️ 注意:Nuxt Server 路由在 Vercel 上运行在 Node.js Serverless Functions 中(不是 Edge Runtime)。Prisma 等依赖 Node.js 绑定的库可以正常工作。如果你想把部分逻辑放到 Edge,需要手动编写 Vercel Edge Middleware(与 Nuxt Server 路由是两套体系)。

2.4 获取请求体和查询参数

// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
  // 查询参数:?category=tech
  const query = getQuery(event);
  const category = query.category as string;

  // 请求体(JSON)
  const body = await readBody(event);

  // 表单数据
  const formData = await readMultipartFormData(event);

  return { category, body };
});

三、部署前的准备

3.1 环境变量配置

创建 .env

NUXT_DATABASE_URL="postgresql://user:pass@host:5432/db"
NUXT_API_SECRET="your-secret-key"
NUXT_PUBLIC_APP_NAME="My Nuxt App"

Nuxt 的环境变量分为两类:

前缀作用域示例
NUXT_NUXT_PRIVATE_服务端专用数据库连接、API Secret
NUXT_PUBLIC_服务端 + 客户端共享App 名称、API Base URL

在代码中读取:

// 服务端(Server Route、SSR 组件)
const config = useRuntimeConfig();
console.log(config.databaseUrl);     // 服务端私有
console.log(config.public.appName);  // 客户端也可访问
<!-- 客户端组件 -->
<template>
  <h1>{{ appName }}</h1>
</template>

<script setup>
const appName = useRuntimeConfig().public.appName;
</script>

3.2 Vercel 环境变量设置

  1. Vercel Dashboard → 项目 → Settings → Environment Variables
  2. 添加所有 NUXT_NUXT_PUBLIC_ 开头的变量
  3. Production 和 Preview 环境都配置一遍(或至少 Production)

3.3 构建配置检查

Vercel 会自动识别 Nuxt 项目,但建议确认构建配置:

  • Build Command: npm run build(或 nuxt build
  • Output Directory: .output/public
  • Install Command: npm install

如果 nuxt.config.ts 中正确设置了 preset: 'vercel',Vercel 会自动生成 .vercel/output/functions/ 下的 Functions 打包文件。


四、在 Vercel 上部署

4.1 Git 推送自动部署

git init
git add .
git commit -m "Init Nuxt project"
git remote add origin https://github.com/yourname/my-nuxt-app.git
git push -u origin main
  1. 在 Vercel Dashboard 点击 “New Project”
  2. 导入 GitHub 仓库 my-nuxt-app
  3. Vercel 自动识别为 Nuxt 项目,保持默认构建配置
  4. 确认 Environment Variables 已添加
  5. 点击 “Deploy”,等待构建完成

4.2 手动部署(Vercel CLI)

# 安装 Vercel CLI
npm i -g vercel

# 登录
vercel login

# 在项目根目录执行
vercel

# 首次会提示配置,后续推送自动部署
# 部署到生产环境
vercel --prod

4.3 验证部署

构建完成后,访问 https://my-nuxt-app.vercel.app 验证:

# 测试首页
curl https://my-nuxt-app.vercel.app

# 测试 API
curl https://my-nuxt-app.vercel.app/api/hello

# 测试带参数 API
curl https://my-nuxt-app.vercel.app/api/users/123

五、图片与静态资源优化

5.1 Nuxt Image 模块

npm install -D @nuxt/image
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    domains: ['cdn.yourdomain.com'],
    screens: {
      xs: 320,
      sm: 640,
      md: 768,
      lg: 1024,
      xl: 1280,
    },
  },
});
<!-- 自动响应式图片 -->
<NuxtImg
  src="/hero.jpg"
  alt="Hero"
  sizes="xs:100vw sm:50vw md:400px"
  loading="lazy"
  placeholder
/>

@nuxt/image 在 Vercel 上会自动使用 Vercel Image Optimization API,无需配置第三方图片 CDN。

5.2 静态资源缓存

Nuxt 的 .output/public/ 目录下的文件会被 Vercel 自动上传到 CDN。为了进一步优化缓存:

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    routeRules: {
      '/_nuxt/**': {
        headers: {
          'cache-control': 'public, max-age=31536000, immutable',
        },
      },
      '/images/**': {
        headers: {
          'cache-control': 'public, max-age=604800',
        },
      },
    },
  },
});

六、ISR 与缓存策略

6.1 ISR 配置详解

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // 首页:ISR 60 秒
    '/': { isr: 60 },

    // 内容页:ISR 1 小时
    '/posts/**': { isr: 3600 },

    // 用户页:SSR(不缓存)
    '/user/**': { isr: false },

    // API:CORS + 不缓存
    '/api/**': { isr: false, cors: true },
  },
});

ISR 在 Vercel 上的行为:

  • 首次请求:Serverless Function 渲染,返回 HTML,同时缓存到 Vercel CDN
  • 缓存期内(如 60 秒):后续请求直接命中 CDN,Vercel 不计费 Serverless 执行
  • 缓存过期后:下一个请求会触发背景重新生成(stale-while-revalidate),用户仍收到旧缓存

6.2 On-Demand Revalidation(手动失效)

// server/api/revalidate.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  const { path, token } = body;

  // 校验密钥
  const config = useRuntimeConfig();
  if (token !== config.revalidateToken) {
    throw createError({ statusCode: 401, statusMessage: 'Invalid token' });
  }

  // 触发重新验证
  const nitro = useNitroApp();
  await nitio.hooks.callHook(' nitro:cache:invalidate', { path });

  return { revalidated: true };
});

从 CMS 或后台管理触发失效:

curl -X POST https://my-nuxt-app.vercel.app/api/revalidate \
  -H "Content-Type: application/json" \
  -d '{"path": "/posts/hello-world", "token": "your-secret"}'

七、常见坑点与排查

7.1 “404 Not Found” 部署后

原因:Vercel 没有正确识别 Nuxt 的 output 目录。

修复

  1. 确认 nuxt.config.ts 中有 nitro: { preset: 'vercel' }
  2. 检查 Vercel Dashboard → Settings → General → Output Directory 是否设为 .output/public
  3. 如果输出目录为空,检查 npm run build 是否成功生成了 .output/ 目录

7.2 API 路由返回 500

排查步骤

  1. Vercel Dashboard → Logs → 选择你的项目 → 查看最近的 Function Error
  2. 常见原因:环境变量缺失(NUXT_DATABASE_URL 没配置)、Prisma 生成未执行
  3. 如果是 Prisma 问题,在构建命令中加 prisma generate
// package.json
{
  "scripts": {
    "build": "prisma generate && nuxt build",
    "postinstall": "prisma generate"
  }
}

7.3 环境变量在客户端读取不到

原因:客户端只能访问 NUXT_PUBLIC_ 前缀的环境变量。

确认

  • 服务端 .envNUXT_PUBLIC_API_BASE=https://api.example.com
  • 客户端代码:useRuntimeConfig().public.apiBase
  • 服务端代码:useRuntimeConfig().apiBase ❌(非 public 前缀在客户端不可见)

7.4 构建时间过长

  • 减少 node_modules 体积:npm prune --production 后上传
  • 启用 swc(Nuxt 3 默认已启用)
  • 对于 monorepo,使用 Nx/Turborepo 远程缓存

八、从 Demo 到生产

8.1 添加自定义域名

参照 Vercel 国内访问优化指南,配置 www.yourdomain.com + Cloudflare Proxy,确保中国大陆访问稳定。

8.2 添加监控

# 安装 Vercel Analytics
npm install -D @vercel/analytics
// nuxt.config.ts
export default defineNuxtConfig({
  plugins: [
    { src: '~/plugins/analytics.client.ts', mode: 'client' },
  ],
});
// plugins/analytics.client.ts
import { inject } from '@vercel/analytics';

export default defineNuxtPlugin(() => {
  inject();
});

8.3 安全头配置

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    routeRules: {
      '/**': {
        headers: {
          'X-Frame-Options': 'DENY',
          'X-Content-Type-Options': 'nosniff',
          'Referrer-Policy': 'strict-origin-when-cross-origin',
          'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'",
        },
      },
    },
  },
});

常见问题(FAQ)

Nuxt 2 和 Nuxt 3 在 Vercel 上有什么不同?

Nuxt 3 使用 Nitro 引擎,原生支持 Vercel preset,部署零配置。Nuxt 2 需要 vercel-builder 适配器,且性能不如 Nitro。新项目请用 Nuxt 3。

Nuxt 在 Vercel 上能跑 Edge Functions 吗?

Nuxt 的 Server Routes 默认运行在传统 Serverless Functions(Node.js Runtime)。如果确实需要 Edge Runtime,可以用 Vercel 的独立 Edge Middleware(middleware.ts),与 Nuxt 应用并行部署。

Nuxt 的 useFetch 在 SSR 时请求的是哪里?

在 Vercel 的 SSR 模式下,useFetch('/api/posts') 会在服务器端直接调用 Nitro 内部的 API 路由(不走 HTTP),性能最优。在客户端(hydration 后),useFetch 会走正常的 HTTP 请求。

Vercel 对 Nuxt 的 ISR 支持如何?

完全支持。Nuxt 的 routeRules.isrpreset: 'vercel' 下会自动使用 Vercel 的 Incremental Static Regeneration 缓存层,行为和 Next.js ISR 类似。

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「saas」更多文章