03. 环境变量管理
环境变量是区分开发、测试、生产环境的关键。Next.js 提供了强大的环境变量管理机制,但也有一些安全陷阱需要注意。
1. 环境变量文件
Next.js 支持多种环境变量文件,按优先级从高到低:
| 文件名 | 使用场景 | 优先级 |
|---|---|---|
.env.local | 本地开发,不应该提交到 Git | 最高 |
.env.development | 开发环境 | 中 |
.env.production | 生产环境 | 中 |
.env | 所有环境通用 | 最低 |
文件加载顺序
Next.js 会按以下顺序加载环境变量:
.env(所有环境).env.development或.env.production(根据NODE_ENV).env.local(所有环境,但优先级最高)
重要:后加载的变量会覆盖先加载的变量。
2. 客户端 vs 服务端变量
这是 Next.js 环境变量管理的核心概念。
服务端变量(默认)
所有环境变量默认只在服务端可用,不会暴露到客户端。
# .env.local
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=super-secret-key-123// ✅ 服务端组件可以使用
export default async function Page() {
const dbUrl = process.env.DATABASE_URL // ✅ 可以访问
return <div>...</div>
}
// ❌ 客户端组件不能使用
'use client'
export default function ClientComponent() {
const dbUrl = process.env.DATABASE_URL // ❌ undefined
return <div>...</div>
}客户端变量(需要前缀)
如果变量需要在客户端使用,必须加上 NEXT_PUBLIC_ 前缀。
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_APP_NAME=My App// ✅ 客户端组件可以使用
'use client'
export default function ClientComponent() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL // ✅ 可以访问
return <div>{apiUrl}</div>
}⚠️ 安全警告:
NEXT_PUBLIC_前缀的变量会被打包到客户端 JS 中,任何人都可以在浏览器中看到。永远不要把敏感信息(如 API 密钥、数据库密码)放在NEXT_PUBLIC_变量中。
3. 实战案例
案例 1:API 配置
场景:开发环境使用本地 API,生产环境使用远程 API。
# .env.development
NEXT_PUBLIC_API_URL=http://localhost:3001/api
API_SECRET_KEY=dev-secret-key
# .env.production
NEXT_PUBLIC_API_URL=https://api.production.com
API_SECRET_KEY=prod-secret-key-xxx使用:
// 客户端组件
'use client'
export default function ApiComponent() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL
const fetchData = async () => {
const res = await fetch(`${apiUrl}/users`)
return res.json()
}
return <button onClick={fetchData}>获取数据</button>
}
// 服务端组件
export default async function ServerPage() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL
const secretKey = process.env.API_SECRET_KEY // ✅ 服务端可用
const res = await fetch(`${apiUrl}/users`, {
headers: {
'Authorization': `Bearer ${secretKey}` // 安全:密钥不会暴露到客户端
}
})
const data = await res.json()
return <div>{/* 渲染数据 */}</div>
}案例 2:数据库连接
场景:数据库连接字符串不应该暴露到客户端。
# .env.local(不提交到 Git)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb// ✅ 只能在服务端使用
// app/api/users/route.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL, // ✅ 服务端安全
},
},
})
export async function GET() {
const users = await prisma.user.findMany()
return Response.json(users)
}案例 3:第三方服务配置
场景:集成 Stripe 支付(需要公钥和私钥)。
# .env.local
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx// 客户端:使用公钥
'use client'
import { loadStripe } from '@stripe/stripe-js'
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
)
export default function CheckoutButton() {
const handleCheckout = async () => {
const stripe = await stripePromise
// 使用公钥初始化 Stripe
}
return <button onClick={handleCheckout}>支付</button>
}
// 服务端:使用私钥
// app/api/checkout/route.ts
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
})
export async function POST(request: Request) {
// 使用私钥创建支付意图
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
})
return Response.json({ clientSecret: paymentIntent.client_secret })
}4. 类型安全的环境变量
创建类型定义
// src/types/env.d.ts
namespace NodeJS {
interface ProcessEnv {
// 客户端变量
NEXT_PUBLIC_API_URL: string
NEXT_PUBLIC_APP_NAME: string
// 服务端变量
DATABASE_URL: string
API_SECRET_KEY: string
}
}使用验证库(推荐)
使用 zod 验证环境变量,确保类型安全:
npm install zod// src/lib/env.ts
import { z } from 'zod'
const envSchema = z.object({
// 客户端变量
NEXT_PUBLIC_API_URL: z.string().url(),
NEXT_PUBLIC_APP_NAME: z.string(),
// 服务端变量
DATABASE_URL: z.string().url(),
API_SECRET_KEY: z.string().min(1),
})
// 验证并导出
export const env = envSchema.parse({
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME,
DATABASE_URL: process.env.DATABASE_URL,
API_SECRET_KEY: process.env.API_SECRET_KEY,
})
// 使用
// import { env } from '@/lib/env'
// const apiUrl = env.NEXT_PUBLIC_API_URL // 类型安全5. .gitignore 配置
确保敏感信息不会被提交到 Git:
# .gitignore
# 环境变量文件
.env*.local
.env.local
.env.development.local
.env.test.local
.env.production.local
# 但保留示例文件
!.env.example创建 .env.example 作为模板:
# .env.example
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=your-secret-key团队成员可以复制 .env.example 为 .env.local 并填入实际值。
6. 运行时环境变量
在 Vercel 中配置
- 进入项目设置
- 找到 “Environment Variables”
- 添加变量(会自动区分开发/预览/生产环境)
在 Docker 中配置
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# 构建时传入环境变量
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build
CMD ["npm", "start"]# 运行容器时传入
docker build --build-arg NEXT_PUBLIC_API_URL=https://api.example.com .7. 常见错误和解决方案
错误 1:客户端访问服务端变量
// ❌ 错误
'use client'
export default function Component() {
const secret = process.env.API_SECRET_KEY // undefined
return <div>{secret}</div>
}
// ✅ 正确:通过 API 路由获取
'use client'
export default function Component() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/secret-data')
.then(res => res.json())
.then(setData)
}, [])
return <div>{data}</div>
}错误 2:忘记 NEXT_PUBLIC_ 前缀
// ❌ 错误
'use client'
const apiUrl = process.env.API_URL // undefined
// ✅ 正确
const apiUrl = process.env.NEXT_PUBLIC_API_URL错误 3:在构建时使用运行时变量
// ❌ 错误:构建时 NEXT_PUBLIC_API_URL 可能不存在
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'default'
// ✅ 正确:使用验证
import { env } from '@/lib/env'
const apiUrl = env.NEXT_PUBLIC_API_URL8. 实战练习:配置多环境项目
需求
创建一个支持开发、测试、生产三环境的项目配置。
实现步骤
步骤 1:创建环境变量文件
# .env.development
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_ENV=development
# .env.production
NEXT_PUBLIC_API_URL=https://api.production.com
NEXT_PUBLIC_ENV=production
# .env.local(不提交)
DATABASE_URL=postgresql://localhost:5432/devdb步骤 2:创建类型定义
// src/types/env.d.ts
namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_API_URL: string
NEXT_PUBLIC_ENV: 'development' | 'production' | 'test'
DATABASE_URL: string
}
}步骤 3:使用环境变量
// src/app/page.tsx
export default function Home() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL
const env = process.env.NEXT_PUBLIC_ENV
return (
<div>
<h1>当前环境:{env}</h1>
<p>API 地址:{apiUrl}</p>
</div>
)
}9. 总结
本章我们学习了:
- ✅ 环境变量文件的优先级和加载顺序
- ✅ 客户端变量(
NEXT_PUBLIC_)和服务端变量的区别 - ✅ 如何安全地管理敏感信息
- ✅ 类型安全的环境变量验证
- ✅ 多环境配置最佳实践
安全要点:
- 🔒 永远不要把敏感信息放在
NEXT_PUBLIC_变量中 - 🔒
.env.local不要提交到 Git - 🔒 使用
zod验证环境变量,确保类型安全
下一步:在下一章,我们将通过实战创建多个页面,深入学习 Next.js 的路由系统。