Nuxt 生产环境部署与监控:从开发到上线的完整实践
开发完成只是第一步——让应用在生产环境稳定运行才是真正的挑战。本文将带你走完 Nuxt 3 应用从构建到监控的全流程,避开那些"线上才出现"的坑。
部署方案对比
Nuxt 3 支持多种部署模式,选择取决于你的场景:
SSR 服务端渲染
# 构建 SSR 应用
npm run build
# 输出目录
.output/
├── server/ # Node.js 服务
├── public/ # 静态资源
└── nitro.json # 运行时配置
适用场景:
- 需要 SEO 的内容站
- 首屏性能要求高
- 有动态数据渲染需求
部署要求:
- Node.js 运行时
- 持久化进程管理(PM2/Docker)
SSG 静态生成
# 预渲染所有页面
npm run generate
# 输出纯静态文件
.output/public/
├── index.html
├── about.html
├── _nuxt/ # JS/CSS 资源
└── ...
适用场景:
- 博客、文档站
- 内容变化不频繁
- 追求极致性能和成本
部署要求:
- 任何静态托管(Nginx/CDN/Vercel)
- 无需 Node.js
混合模式
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }, // 首页静态生成
'/blog/**': { swr: 3600 }, // 博客缓存 1 小时
'/dashboard/**': { ssr: false }, // 后台纯客户端
'/api/**': { cors: true } // API 开启 CORS
}
})
生产构建配置
环境变量管理
# .env.production
NUXT_PUBLIC_API_BASE=https://api.example.com
NUXT_API_SECRET=your-secret-key
DATABASE_URL=postgresql://...
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// 仅服务端可访问
apiSecret: '',
databaseUrl: '',
// 客户端也可访问
public: {
apiBase: ''
}
}
})
使用:
// 组件/页面中
const config = useRuntimeConfig()
const apiBase = config.public.apiBase // 客户端安全
// server/api 中
const secret = config.apiSecret // 仅服务端
构建优化
// nuxt.config.ts
export default defineNuxtConfig({
// 压缩配置
vite: {
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true
}
}
}
},
// Nitro 服务端优化
nitro: {
compressPublicAssets: true, // 压缩静态资源
minify: true
},
// 实验性功能
experimental: {
payloadExtraction: true, // 提取页面 payload
viewTransition: true
}
})
部署到各平台
Vercel(推荐 Serverless)
# 安装 Vercel CLI
npm i -g vercel
# 部署
vercel
或连接 Git 仓库自动部署:
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".output",
"framework": "nuxt"
}
Docker 部署
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.output .output
COPY --from=builder /app/package.json .
ENV NODE_ENV=production
ENV NUXT_HOST=0.0.0.0
ENV NUXT_PORT=3000
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]
# docker-compose.yml
version: '3.8'
services:
nuxt-app:
build: .
ports:
- "3000:3000"
environment:
- NUXT_PUBLIC_API_BASE=https://api.example.com
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
PM2 部署(传统服务器)
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'nuxt-app',
script: '.output/server/index.mjs',
instances: 'max', // 多进程
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
NUXT_HOST: '0.0.0.0',
NUXT_PORT: 3000
},
max_memory_restart: '500M',
error_file: './logs/err.log',
out_file: './logs/out.log',
time: true
}
]
}
# 启动
pm2 start ecosystem.config.js
# 查看状态
pm2 status
# 监控
pm2 monit
Nginx 反向代理
# /etc/nginx/sites-available/nuxt-app
upstream nuxt_backend {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
# SSL 配置
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 静态资源缓存
location /_nuxt/ {
proxy_pass http://nuxt_backend;
proxy_cache_valid 200 365d;
add_header Cache-Control "public, immutable";
}
# 其他请求
location / {
proxy_pass http://nuxt_backend;
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;
}
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
}
健康检查与监控
健康检查端点
// server/api/health.get.ts
export default defineEventHandler(() => {
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage()
}
})
性能监控集成
// plugins/monitoring.client.ts
export default defineNuxtPlugin(() => {
// Web Vitals 上报
if (typeof window !== 'undefined') {
import('web-vitals').then(({ onCLS, onFID, onLCP, onFCP, onTTFB }) => {
const reportVital = (metric) => {
// 上报到分析服务
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric)
})
}
onCLS(reportVital)
onFID(reportVital)
onLCP(reportVital)
onFCP(reportVital)
onTTFB(reportVital)
})
}
})
错误追踪(Sentry)
npm install @sentry/vue
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
Sentry.init({
app: nuxtApp.vueApp,
dsn: config.public.sentryDsn,
environment: config.public.env,
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration()
]
})
})
// server/plugins/sentry.ts
import * as Sentry from '@sentry/node'
export default defineNitroPlugin((nitroApp) => {
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1
})
nitroApp.hooks.hook('error', (error) => {
Sentry.captureException(error)
})
})
日志管理
// server/utils/logger.ts
import pino from 'pino'
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty' }
: undefined,
base: {
env: process.env.NODE_ENV
}
})
// 使用
logger.info({ userId: 123 }, 'User logged in')
logger.error({ err }, 'Request failed')
性能优化实践
缓存策略
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// CDN 缓存
'/_nuxt/**': {
headers: { 'Cache-Control': 'public, max-age=31536000, immutable' }
},
// API 响应缓存
'/api/products': {
cache: { maxAge: 60 * 5 } // 5 分钟
},
// SWR 模式
'/blog/**': {
swr: 3600, // 后台重新验证
cache: { maxAge: 3600 }
}
}
})
CDN 配置
// nuxt.config.ts
export default defineNuxtConfig({
app: {
cdnURL: 'https://cdn.example.com' // 静态资源走 CDN
}
})
数据库连接池
// server/utils/db.ts
import { Pool } from 'pg'
let pool: Pool | null = null
export function getPool() {
if (!pool) {
pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // 最大连接数
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
})
}
return pool
}
// 优雅关闭
process.on('SIGTERM', async () => {
if (pool) await pool.end()
})
CI/CD 流水线
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
NUXT_PUBLIC_API_BASE: ${{ secrets.API_BASE }}
- name: Run tests
run: npm test
- name: Build Docker image
run: docker build -t nuxt-app:${{ github.sha }} .
- name: Push to registry
run: |
docker tag nuxt-app:${{ github.sha }} registry.example.com/nuxt-app:latest
docker push registry.example.com/nuxt-app:latest
- name: Deploy to server
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
docker pull registry.example.com/nuxt-app:latest
docker-compose up -d
零停机部署
#!/bin/bash
# deploy.sh
# 拉取新镜像
docker pull registry.example.com/nuxt-app:latest
# 启动新容器(不同端口)
docker run -d --name nuxt-app-new -p 3001:3000 nuxt-app:latest
# 等待健康检查通过
until curl -f http://localhost:3001/api/health; do
sleep 2
done
# 切换 Nginx 上游
sed -i 's/127.0.0.1:3000/127.0.0.1:3001/' /etc/nginx/sites-available/nuxt-app
nginx -s reload
# 停止旧容器
docker stop nuxt-app-old
docker rm nuxt-app-old
# 重命名容器
docker rename nuxt-app-new nuxt-app-old
故障排查
常见问题
1. 内存泄漏
# 监控内存
pm2 monit
# 设置内存限制自动重启
pm2 start app.js --max-memory-restart 500M
2. 冷启动慢
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
// 预热关键路由
prerender: {
routes: ['/', '/about', '/products']
}
}
})
3. SSR 水合失败
<template>
<!-- 客户端专属内容 -->
<ClientOnly>
<BrowserOnlyComponent />
</ClientOnly>
</template>
调试工具
# 开启详细日志
DEBUG=nitro:* node .output/server/index.mjs
# 分析构建产物
npx nuxi analyze
# 性能分析
node --inspect .output/server/index.mjs
监控仪表盘
关键指标
## 服务健康
- 可用性 (Uptime): 99.9%
- 响应时间 P99: < 500ms
- 错误率: < 0.1%
## 资源使用
- CPU: < 70%
- 内存: < 80%
- 磁盘 I/O: 正常
## 业务指标
- QPS: 当前请求数
- 活跃用户: 实时在线
- 转化漏斗: 关键路径
告警规则
# prometheus/alerts.yml
groups:
- name: nuxt-app
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
for: 5m
annotations:
summary: "错误率超过 1%"
- alert: HighResponseTime
expr: histogram_quantile(0.99, http_request_duration_seconds) > 0.5
for: 5m
annotations:
summary: "P99 响应时间超过 500ms"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes > 500000000
for: 10m
annotations:
summary: "内存使用超过 500MB"
总结
Nuxt 生产部署检查清单:
| 阶段 | 检查项 |
|---|---|
| 构建 | 环境变量配置、构建优化、移除 console |
| 部署 | Docker/PM2 配置、Nginx 反代、SSL |
| 监控 | 健康检查、错误追踪、性能指标 |
| 维护 | 日志管理、告警规则、备份策略 |
| CI/CD | 自动化流水线、零停机部署 |
生产环境没有"差不多"——每个细节都可能在凌晨 3 点把你叫醒。提前规划、持续监控、快速响应,才是运维的正道。


