如何为不同环境(开发、预览、生产)配置不同的变量?
环境配置策略
1. 环境变量文件
Next.js 支持按环境自动加载不同的环境变量文件:
# .env.local (最高优先级,所有环境)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=your-secret-key
# .env.development (开发环境)
DEBUG=true
LOG_LEVEL=debug
API_URL=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3001
# .env.production (生产环境)
DEBUG=false
LOG_LEVEL=error
API_URL=https://api.example.com
NEXT_PUBLIC_API_URL=https://api.example.com
# .env.preview (预览环境)
DEBUG=true
LOG_LEVEL=info
API_URL=https://preview-api.example.com
NEXT_PUBLIC_API_URL=https://preview-api.example.com
2. 环境检测
// lib/env.js
export const isDevelopment = process.env.NODE_ENV === 'development'
export const isProduction = process.env.NODE_ENV === 'production'
export const isPreview = process.env.VERCEL_ENV === 'preview'
export const getEnvironment = () => {
if (isDevelopment) return 'development'
if (isPreview) return 'preview'
if (isProduction) return 'production'
return 'unknown'
}
配置管理
1. 统一配置对象
// lib/config.js
const baseConfig = {
appName: 'My App',
version: '1.0.0',
}
const developmentConfig = {
...baseConfig,
apiUrl: 'http://localhost:3001',
databaseUrl: 'postgresql://user:password@localhost:5432/mydb_dev',
debug: true,
logLevel: 'debug',
features: {
analytics: false,
monitoring: false,
caching: false,
},
}
const previewConfig = {
...baseConfig,
apiUrl: 'https://preview-api.example.com',
databaseUrl: 'postgresql://user:password@preview-db:5432/mydb_preview',
debug: true,
logLevel: 'info',
features: {
analytics: true,
monitoring: true,
caching: true,
},
}
const productionConfig = {
...baseConfig,
apiUrl: 'https://api.example.com',
databaseUrl: 'postgresql://user:password@prod-db:5432/mydb_prod',
debug: false,
logLevel: 'error',
features: {
analytics: true,
monitoring: true,
caching: true,
},
}
export const getConfig = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
if (env === 'development') return developmentConfig
if (vercelEnv === 'preview') return previewConfig
if (env === 'production') return productionConfig
return developmentConfig
}
export const config = getConfig()
2. 环境特定配置
// lib/env-specific.js
export const getApiUrl = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
if (env === 'development') {
return process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'
}
if (vercelEnv === 'preview') {
return process.env.NEXT_PUBLIC_API_URL || 'https://preview-api.example.com'
}
if (env === 'production') {
return process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com'
}
return 'http://localhost:3001'
}
export const getDatabaseUrl = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
if (env === 'development') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@localhost:5432/mydb_dev'
)
}
if (vercelEnv === 'preview') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@preview-db:5432/mydb_preview'
)
}
if (env === 'production') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@prod-db:5432/mydb_prod'
)
}
return 'postgresql://user:password@localhost:5432/mydb_dev'
}
实际应用示例
1. 数据库配置
// lib/database.js
import { PrismaClient } from '@prisma/client'
const getDatabaseUrl = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
if (env === 'development') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@localhost:5432/mydb_dev'
)
}
if (vercelEnv === 'preview') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@preview-db:5432/mydb_preview'
)
}
if (env === 'production') {
return (
process.env.DATABASE_URL ||
'postgresql://user:password@prod-db:5432/mydb_prod'
)
}
return 'postgresql://user:password@localhost:5432/mydb_dev'
}
const globalForPrisma = globalThis
export const db =
globalForPrisma.prisma ||
new PrismaClient({
datasources: {
db: {
url: getDatabaseUrl(),
},
},
log:
process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error'],
})
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = db
}
2. API 客户端配置
// lib/api-client.js
const getApiConfig = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
const baseConfig = {
timeout: 10000,
retries: 3,
}
if (env === 'development') {
return {
...baseConfig,
baseURL: 'http://localhost:3001',
timeout: 30000, // 开发环境更长的超时时间
retries: 1,
}
}
if (vercelEnv === 'preview') {
return {
...baseConfig,
baseURL: 'https://preview-api.example.com',
timeout: 15000,
}
}
if (env === 'production') {
return {
...baseConfig,
baseURL: 'https://api.example.com',
timeout: 10000,
}
}
return baseConfig
}
export const apiClient = axios.create(getApiConfig())
3. 日志配置
// lib/logger.js
const getLogLevel = () => {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
if (env === 'development') return 'debug'
if (vercelEnv === 'preview') return 'info'
if (env === 'production') return 'error'
return 'info'
}
export const logger = {
debug: (message, ...args) => {
if (getLogLevel() === 'debug') {
console.debug(`[DEBUG] ${message}`, ...args)
}
},
info: (message, ...args) => {
if (['debug', 'info'].includes(getLogLevel())) {
console.info(`[INFO] ${message}`, ...args)
}
},
error: (message, ...args) => {
console.error(`[ERROR] ${message}`, ...args)
},
}
部署配置
1. Vercel 环境配置
# vercel.json
{
"env": {
"DATABASE_URL": "@database-url",
"JWT_SECRET": "@jwt-secret"
},
"build": {
"env": {
"NEXT_PUBLIC_API_URL": "@api-url"
}
}
}
# 设置环境变量
vercel env add DATABASE_URL
vercel env add JWT_SECRET
vercel env add NEXT_PUBLIC_API_URL
# 为不同环境设置不同值
vercel env add DATABASE_URL --environment development
vercel env add DATABASE_URL --environment preview
vercel env add DATABASE_URL --environment production
2. Docker 环境配置
# Dockerfile
FROM node:18-alpine AS base
# 开发环境
FROM base AS development
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
# 生产环境
FROM base AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["npm", "start"]
# docker-compose.yml
version: '3.8'
services:
app-dev:
build:
context: .
target: development
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@localhost:5432/mydb_dev
ports:
- '3000:3000'
app-prod:
build:
context: .
target: production
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@prod-db:5432/mydb_prod
ports:
- '3001:3000'
3. 环境验证
// lib/env-validation.js
import { z } from 'zod'
const baseSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
})
const developmentSchema = baseSchema.extend({
DEBUG: z.boolean().optional(),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).optional(),
})
const productionSchema = baseSchema.extend({
DEBUG: z.literal(false),
LOG_LEVEL: z.enum(['error']).optional(),
})
export const validateEnv = () => {
const env = process.env.NODE_ENV
try {
if (env === 'development') {
return developmentSchema.parse(process.env)
}
if (env === 'production') {
return productionSchema.parse(process.env)
}
return baseSchema.parse(process.env)
} catch (error) {
console.error('Environment validation failed:', error)
throw new Error('Invalid environment configuration')
}
}
最佳实践
1. 配置模板
# .env.example
# 开发环境
NODE_ENV=development
DATABASE_URL=postgresql://user:password@localhost:5432/mydb_dev
JWT_SECRET=your-development-secret
NEXT_PUBLIC_API_URL=http://localhost:3001
# 预览环境
# NODE_ENV=production
# VERCEL_ENV=preview
# DATABASE_URL=postgresql://user:password@preview-db:5432/mydb_preview
# JWT_SECRET=your-preview-secret
# NEXT_PUBLIC_API_URL=https://preview-api.example.com
# 生产环境
# NODE_ENV=production
# DATABASE_URL=postgresql://user:password@prod-db:5432/mydb_prod
# JWT_SECRET=your-production-secret
# NEXT_PUBLIC_API_URL=https://api.example.com
2. 配置管理
// lib/config-manager.js
export class ConfigManager {
constructor() {
this.config = this.loadConfig()
}
loadConfig() {
const env = process.env.NODE_ENV
const vercelEnv = process.env.VERCEL_ENV
const baseConfig = {
appName: 'My App',
version: '1.0.0',
}
if (env === 'development') {
return {
...baseConfig,
apiUrl: 'http://localhost:3001',
debug: true,
logLevel: 'debug',
}
}
if (vercelEnv === 'preview') {
return {
...baseConfig,
apiUrl: 'https://preview-api.example.com',
debug: true,
logLevel: 'info',
}
}
if (env === 'production') {
return {
...baseConfig,
apiUrl: 'https://api.example.com',
debug: false,
logLevel: 'error',
}
}
return baseConfig
}
get(key) {
return this.config[key]
}
getAll() {
return this.config
}
}
export const configManager = new ConfigManager()
总结
环境配置管理要点:
环境变量文件:
.env.local(最高优先级).env.development/.env.production.env.preview(预览环境)
配置策略:
- 统一配置对象
- 环境特定配置
- 配置验证
最佳实践:
- 使用配置模板
- 环境变量验证
- 配置管理类
- 部署环境配置
安全考虑:
- 敏感信息不要使用
NEXT_PUBLIC_前缀 - 不同环境使用不同的密钥
- 定期轮换生产环境密钥