多平台部署策略与选型
Nuxt4 的部署灵活性是框架的核心竞争力之一。同一份代码,通过 Nitro 的 preset 机制,可以部署到 Node.js 服务器、Docker 容器、Serverless 平台、边缘节点甚至生成纯静态站点。部署方式的选择直接影响成本、性能、运维复杂度和扩展性。本章从 Nuxt4 的渲染模式和构建产物出发,系统对比主流部署方案的取舍。
1. 渲染模式与部署的关系
1.1 三种渲染模式
在讨论部署之前,必须先理解渲染模式——它决定了构建产物的形态:
| 渲染模式 | 构建产物 | 运行时需求 | 适用场景 |
|---|---|---|---|
| SSR(默认) | Node.js 服务端代码 + 客户端 bundle | 需要 Node.js 服务器 | 动态内容、SEO、个性化 |
SSG(nuxi generate) | 纯静态 HTML + JS/CSS | 仅需静态文件服务 | 博客、文档、营销页 |
| SPA | 单个 HTML + JS bundle | 仅需静态文件服务 | 后台管理系统 |
关键认知:渲染模式决定了可选的部署平台范围。SSG/SPA 可以部署到任何静态托管(CDN、OSS、GitHub Pages),而 SSR 必须有运行时环境(Node.js / Serverless / Edge)。
1.2 Nitro Preset:一键切换部署目标
Nitro 是 Nuxt4 的服务端引擎,它的 preset 机制让同一份代码适配不同平台:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'node-server', // 或 'vercel', 'cloudflare', 'netlify' 等
},
})Nitro 会根据 preset 生成对应平台的部署产物——Node.js 的生成可执行文件,Vercel 的生成 Serverless Functions,Cloudflare 的生成 Workers 脚本。你不需要修改业务代码,只需切换 preset。
2. Node.js 服务器部署
2.1 适用场景
- 需要完整 Node.js 运行时能力(文件系统、长连接、WebSocket)
- 自有服务器或云主机(ECS、EC2)
- 需要精细控制进程管理、内存、CPU
2.2 构建与运行
# 构建
NITRO_PRESET=node-server nuxi build
# 产物结构
.output/
├── server/
│ └── index.mjs ← Node.js 入口
├── public/ ← 静态资源
└── nitro.json ← 配置# 启动
node .output/server/index.mjs
# 服务器监听 http://localhost:30002.3 PM2 进程管理
生产环境不应该用 node 直接启动——进程崩溃后无法自动重启。PM2 是 Node.js 生态最成熟的进程管理器:
// ecosystem.config.cjs
module.exports = {
apps: [{
name: 'nuxt-app',
script: '.output/server/index.mjs',
instances: 'max', // 使用所有 CPU 核心
exec_mode: 'cluster', // 集群模式
env: {
NODE_ENV: 'production',
PORT: 3000,
NITRO_PORT: 3000,
},
max_memory_restart: '1G',
error_file: './logs/error.log',
out_file: './logs/out.log',
}],
}PM2 集群模式的核心价值:
- 多进程:
instances: 'max'启动与 CPU 核心数相同的进程,充分利用多核 - 零停机重启:
pm2 reload逐个重启进程,始终有进程在处理请求 - 自动重启:进程崩溃后自动拉起,
max_memory_restart防止内存泄漏 - 日志管理:自动轮转日志文件
2.4 Nginx 反向代理
upstream nuxt_app {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nuxt_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# 静态资源直接由 Nginx 提供(绕过 Node.js)
location /_nuxt/ {
alias .output/public/_nuxt/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}关键优化:静态资源由 Nginx 直接提供(/_nuxt/ 路径),不经过 Node.js 进程。这减少了 Node.js 的负载,Nginx 在服务静态文件方面比 Node.js 快一个数量级。
3. Docker 容器化部署
3.1 适用场景
- 需要环境一致性(开发/测试/生产环境完全相同)
- Kubernetes 编排
- 微服务架构
- 团队有容器化运维经验
3.2 多阶段构建
# 阶段 1:依赖安装
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# 阶段 2:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN corepack enable && pnpm build
# 阶段 3:运行时(最小镜像)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.output ./.output
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]多阶段构建的意义:
- deps 阶段:单独安装依赖,利用 Docker 层缓存——依赖不变时跳过此步
- builder 阶段:构建应用,包含完整的 devDependencies
- runner 阶段:只复制
.output,最终镜像不包含node_modules、源代码、构建工具
最终镜像大小:约 80-120MB(Node.js Alpine + .output),而非 1GB+(包含所有 devDependencies)。
3.3 Docker Compose
# docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://db:5432/myapp
- NUXT_SESSION_SECRET=${SESSION_SECRET}
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myapp
- POSTGRES_PASSWORD=${DB_PASSWORD}
redis:
image: redis:7-alpine
volumes:
pgdata:3.4 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --spider -q http://localhost:3000/api/health || exit 1// server/api/health.get.ts
export default defineEventHandler(() => ({
status: 'ok',
timestamp: Date.now(),
}))4. Serverless 部署
4.1 适用场景
- 流量波动大(峰谷差 10 倍以上)
- 不想运维服务器
- 按请求计费更经济
- 全球分布式部署
4.2 Vercel
Nuxt 对 Vercel 有一等支持——自动检测 Nuxt 项目并选择正确的 preset:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'vercel', // 通常自动检测,无需手动设置
},
})Vercel 的部署产物:
- Serverless Functions:每个 server route 变成一个独立的 Lambda 函数
- Edge Functions:可选,将部分逻辑部署到边缘节点
- Static Assets:自动上传到 CDN
优势:零配置部署、自动 HTTPS、Preview Deployments(每个 PR 一个预览环境)、全球 CDN。
局限:函数执行时间限制(Hobby 10s,Pro 60s)、不支持 WebSocket、冷启动延迟。
4.3 Netlify
nitro: {
preset: 'netlify',
}Netlify 与 Vercel 类似,但有一些差异:
- 函数超时更长(Background Functions 可达 15 分钟)
- 内置表单处理(无需后端)
- 不支持 Edge SSR(只有 Edge Functions 用于中间件)
4.4 Cloudflare Pages
nitro: {
preset: 'cloudflare-pages',
}Cloudflare Pages 的独特优势:
- 全球 300+ 边缘节点,延迟极低
- 免费额度慷慨(无限请求数,100,000 次/天构建)
- 与 Cloudflare 生态深度集成(D1 数据库、R2 存储、KV 缓存)
局限:Workers 运行时不是完整的 Node.js——不支持文件系统、某些 Node.js API。需要确保代码兼容 Workers 环境。
4.5 Serverless 冷启动问题
Serverless 的最大痛点是冷启动——函数长时间未被调用后,下次调用需要重新初始化运行时环境,延迟可达 1-3 秒。
缓解方案:
- Provisioned Concurrency(Vercel):保持一定数量的热实例
- Edge Functions:V8 Isolate 冷启动 < 5ms,但 API 受限
- 定时保活:定期 ping 函数,保持热状态(不推荐,浪费资源)
5. 静态站点部署
5.1 SSG 生成
nuxi generate生成纯静态 HTML 文件,可以部署到任何静态托管服务。
5.2 部署目标
| 平台 | 费用 | 特点 |
|---|---|---|
| GitHub Pages | 免费 | 适合开源项目 |
| Cloudflare Pages | 免费 | 全球 CDN,速度快 |
| Vercel | 免费额度 | 自动 Preview Deployments |
| Netlify | 免费额度 | 表单处理、分支部署 |
| 阿里云 OSS + CDN | 按量付费 | 国内访问快 |
| AWS S3 + CloudFront | 按量付费 | 全球覆盖 |
5.3 国内部署方案
国内用户访问海外平台(Vercel、Netlify、Cloudflare)可能较慢。国内部署推荐:
nuxi generate → 上传到 OSS → 配置 CDN 加速域名 → 备案
阿里云 OSS 配置要点:
- 开启静态网站托管
- 配置
index.html为默认首页 - 404 页面也指向
index.html(SPA 路由兼容) - CDN 配置缓存规则:
_nuxt/路径长期缓存,HTML 文件短缓存
6. 混合渲染:Route Rules
6.1 同一项目不同路由不同渲染模式
Nuxt4 最强大的部署特性之一是 Route Rules——同一个项目中,不同路由可以使用不同的渲染策略:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }, // 首页:构建时预渲染
'/blog/**': { isr: 3600 }, // 博客:ISR,1 小时重新生成
'/dashboard/**': { ssr: false }, // 后台:纯 SPA,不需要 SSR
'/api/**': { cors: true, cache: false }, // API:允许 CORS,不缓存
'/static/**': { static: true }, // 静态页:完全静态化
},
})6.2 ISR(Incremental Static Regeneration)
ISR 是 SSG 和 SSR 的折中——页面在首次请求时 SSR 渲染并缓存,后续请求直接返回缓存,过期后后台重新生成:
用户 A 请求 /blog/hello → SSR 渲染 → 缓存 1 小时
用户 B 请求 /blog/hello → 返回缓存(快)
1 小时后用户 C 请求 → 返回旧缓存 + 后台重新渲染
用户 D 请求 → 返回新缓存
ISR 适合内容更新频率不高但需要 SEO 的页面(博客、产品页)。
7. 部署选型决策
7.1 决策矩阵
| 因素 | Node.js + PM2 | Docker + K8s | Vercel/Netlify | Cloudflare | 静态托管 |
|---|---|---|---|---|---|
| 运维复杂度 | 中 | 高 | 零 | 零 | 零 |
| 成本(低流量) | 高(固定成本) | 高 | 免费 | 免费 | 免费 |
| 成本(高流量) | 可控 | 可控 | 可能很贵 | 便宜 | 便宜 |
| 冷启动 | 无 | 无 | 有 | 极低 | 无 |
| Node.js 兼容 | 完整 | 完整 | 完整 | 部分 | 不需要 |
| 全球延迟 | 取决于服务器位置 | 多区域部署 | 好 | 极好 | CDN |
| SSR 支持 | ✅ | ✅ | ✅ | ✅ | ❌ |
| WebSocket | ✅ | ✅ | ❌ | 有限 | ❌ |
7.2 推荐方案
- 个人项目/博客:SSG + Cloudflare Pages(免费、快速)
- 中小型 SaaS:Vercel(零运维、Preview Deployments 提高开发效率)
- 大流量网站:Cloudflare Pages + Workers(全球边缘、成本可控)
- 企业内部系统:Docker + K8s(环境一致性、内网部署)
- 需要完整 Node.js:PM2 + Nginx(WebSocket、文件处理、长任务)
本章小结
- 渲染模式决定部署选项:SSR 需要运行时环境,SSG/SPA 只需静态托管
- Nitro preset:一行配置切换部署目标,业务代码无需修改
- Node.js + PM2:集群模式多核利用 + 零停机重启 + Nginx 反向代理静态资源
- Docker:多阶段构建保持镜像精简(~100MB),Docker Compose 管理完整栈
- Serverless:Vercel / Netlify / Cloudflare,零运维但有冷启动和 API 限制
- Route Rules:同一项目不同路由用不同渲染策略(prerender / ISR / SPA / SSR)
- 选型原则:按流量规模、运维能力、Node.js 需求和成本预算综合决策