Next.js 应用如何部署?需要哪些准备工作?output: 'standalone' 选项有什么作用?
部署准备工作
1. 构建优化
// next.config.js
const nextConfig = {
// 启用 standalone 输出
output: 'standalone',
// 优化构建
swcMinify: true,
compress: true,
// 图片优化
images: {
unoptimized: false,
domains: ['example.com'],
},
// 实验性功能
experimental: {
outputFileTracingRoot: path.join(__dirname, '../../'),
},
}
module.exports = nextConfig
2. 环境变量配置
# .env.production
NODE_ENV=production
DATABASE_URL=postgresql://user:password@prod-db:5432/mydb
JWT_SECRET=your-production-secret
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_APP_NAME=My App
3. 依赖优化
// package.json
{
"dependencies": {
"next": "^15.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/react": "^18.0.0",
"typescript": "^5.0.0"
},
"scripts": {
"build": "next build",
"start": "next start",
"dev": "next dev"
}
}
output: 'standalone' 选项
1. 基本概念
// next.config.js
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfig
output: 'standalone' 选项会创建一个独立的部署包,包含:
- 所有必需的依赖
- 优化的服务器代码
- 静态资源
- 运行时配置
2. 构建输出结构
# 构建后的 .next/standalone 目录结构
.next/standalone/
├── server.js # 服务器入口文件
├── package.json # 运行时依赖
├── node_modules/ # 必需的依赖
├── .next/ # 构建输出
│ ├── static/ # 静态资源
│ ├── server/ # 服务器代码
│ └── cache/ # 缓存文件
└── public/ # 公共资源
3. 独立部署
# 构建应用
npm run build
# 启动独立服务器
cd .next/standalone
node server.js
部署方式
1. Vercel 部署
# 安装 Vercel CLI
npm i -g vercel
# 部署到 Vercel
vercel
# 生产环境部署
vercel --prod
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"framework": "nextjs",
"functions": {
"app/api/**/*.js": {
"runtime": "nodejs18.x"
}
}
}
2. Docker 部署
# Dockerfile
FROM node:18-alpine AS base
# 安装依赖
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 构建应用
FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产镜像
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
3. 传统服务器部署
# 构建应用
npm run build
# 复制文件到服务器
scp -r .next/standalone user@server:/var/www/app
scp -r public user@server:/var/www/app
# 在服务器上启动
cd /var/www/app
node server.js
# 使用 PM2 管理进程
npm install -g pm2
# 启动应用
pm2 start server.js --name "nextjs-app"
# 设置开机自启
pm2 startup
pm2 save
4. 云平台部署
AWS Lambda
// next.config.js
const nextConfig = {
output: 'export',
trailingSlash: true,
images: {
unoptimized: true,
},
}
module.exports = nextConfig
# serverless.yml
service: nextjs-app
provider:
name: aws
runtime: nodejs18.x
region: us-east-1
functions:
app:
handler: server.handler
events:
- http:
path: /{proxy+}
method: ANY
- http:
path: /
method: ANY
plugins:
- serverless-nextjs-plugin
Google Cloud Run
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
# 部署到 Cloud Run
gcloud run deploy nextjs-app \
--source . \
--platform managed \
--region us-central1 \
--allow-unauthenticated
性能优化
1. 构建优化
// next.config.js
const nextConfig = {
output: 'standalone',
// 压缩优化
compress: true,
swcMinify: true,
// 图片优化
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
// 实验性功能
experimental: {
optimizeCss: true,
optimizePackageImports: ['@mui/material', 'lodash'],
},
}
2. 缓存策略
// next.config.js
const nextConfig = {
async headers() {
return [
{
source: '/static/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
{
source: '/api/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'no-cache, no-store, must-revalidate',
},
],
},
]
},
}
3. 监控和日志
// lib/monitoring.js
export function setupMonitoring() {
if (process.env.NODE_ENV === 'production') {
// 设置错误监控
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error)
// 发送到监控服务
})
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason)
// 发送到监控服务
})
}
}
最佳实践
1. 环境配置
// lib/env.js
export const env = {
NODE_ENV: process.env.NODE_ENV,
PORT: process.env.PORT || 3000,
DATABASE_URL: process.env.DATABASE_URL,
JWT_SECRET: process.env.JWT_SECRET,
API_URL: process.env.NEXT_PUBLIC_API_URL,
}
// 验证必需的环境变量
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET']
for (const envVar of requiredEnvVars) {
if (!env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`)
}
}
2. 健康检查
// app/api/health/route.js
export async function GET() {
try {
// 检查数据库连接
await db.$queryRaw`SELECT 1`
return NextResponse.json({
status: 'healthy',
timestamp: new Date().toISOString(),
version: process.env.NEXT_PUBLIC_VERSION,
})
} catch (error) {
return NextResponse.json(
{
status: 'unhealthy',
error: error.message,
},
{ status: 500 }
)
}
}
3. 错误处理
// app/error.js
'use client'
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
总结
Next.js 部署要点:
准备工作:
- 构建优化配置
- 环境变量设置
- 依赖管理
output: 'standalone':
- 创建独立部署包
- 包含所有必需依赖
- 简化部署流程
部署方式:
- Vercel (推荐)
- Docker 容器化
- 传统服务器
- 云平台 (AWS, GCP)
最佳实践:
- 性能优化
- 缓存策略
- 监控和日志
- 健康检查
- 错误处理