构建与部署(下)
本教程共 42 篇 · 第 28 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
28. 构建与部署(下)
本节目标:了解 Next.js 的各种部署方式,掌握 Vercel 部署、Docker 容器化和自托管的配置方法,理解多实例部署、CDN 缓存和版本倾斜的处理策略。
部署方式概览
Next.js 支持多种部署方式,每种方式的功能支持程度不同:
| 部署方式 | 功能支持 | 适用场景 |
|---|---|---|
| Node.js 服务器 | 完整支持 | 自托管、传统服务器 |
| Docker 容器 | 完整支持 | Kubernetes、容器编排 |
| 静态导出 | 有限支持 | 纯静态站点 |
| 适配器 | 因平台而异 | 特定平台优化 |
Vercel 部署
Vercel 是 Next.js 的官方托管平台,提供最佳的 Next.js 集成体验。
部署步骤
- 将代码推送到 GitHub/GitLab/Bitbucket
- 在 Vercel 控制台导入项目
- 配置环境变量
- 点击部署
Vercel 会自动检测 Next.js 项目并配置构建命令:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
Vercel 特有功能
- 自动 HTTPS:默认启用
- 全球 CDN:边缘节点分发
- 预览部署:每个 PR 自动生成预览 URL
- 分析:内置 Web Vitals 分析
- 环境变量:按环境(生产/预览/开发)配置
Docker 容器化
Next.js 可以打包为 Docker 容器,部署到任何支持 Docker 的平台。
标准 Dockerfile
# Dockerfile
FROM node:20-alpine AS base
# 依赖安装阶段
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# 构建阶段
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
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 /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
使用 Standalone 输出模式
在 next.config.ts 中启用 standalone 输出,可以生成只包含必要文件的独立构建:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
output: 'standalone',
}
export default nextConfig
使用 standalone 模式后,构建产物只包含运行所需的文件,大幅减小 Docker 镜像体积。
构建并运行 Docker 镜像
# 构建镜像
docker build -t my-nextjs-app .
# 运行容器
docker run -p 3000:3000 my-nextjs-app
自托管
自托管让你完全控制部署环境,适合有特殊需求的企业。
反向代理配置
自托管时推荐使用反向代理(如 nginx)放在 Next.js 服务器前面:
- 处理畸形请求
- 防止慢速连接攻击
- 限制请求体大小
- 实现速率限制
nginx 配置示例
server {
listen 80;
server_name example.com;
# 启用流式传输(禁用缓冲)
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# 禁用缓冲以支持流式响应
proxy_buffering off;
proxy_cache off;
}
}
环境变量处理
自托管时,Next.js 支持构建时和运行时两种环境变量:
// app/page.tsx
import { connection } from 'next/server'
export default async function Page() {
await connection()
// 动态渲染时读取环境变量
const value = process.env.MY_VALUE
return <div>{value}</div>
}
这允许你使用同一个 Docker 镜像在不同环境中运行,只需改变环境变量值。
多实例部署
当 Next.js 运行在多个服务器实例上时(如负载均衡后的多个容器),需要额外配置以确保一致性。
Server Actions 加密密钥
Next.js 会加密 Server Actions 的闭包变量。默认情况下,每次构建都会生成唯一的加密密钥。
多实例部署时,所有实例必须使用相同的加密密钥:
# 生成密钥
openssl rand -base64 32
# 设置环境变量
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY=your-generated-key
部署标识符
配置 deploymentId 可以启用版本倾斜保护:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
deploymentId: process.env.DEPLOYMENT_VERSION,
}
export default nextConfig
当检测到客户端和服务器版本不一致时,Next.js 会触发硬导航(全页面刷新)而不是客户端导航,确保获取一致的静态资源。
共享缓存
默认情况下,Next.js 使用内存缓存,不跨实例共享。多实例部署时,可以配置自定义缓存处理器:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // 禁用默认内存缓存
}
export default nextConfig
// cache-handler.js
const cache = new Map()
module.exports = class CacheHandler {
constructor(options) {
this.options = options
}
async get(key) {
return cache.get(key)
}
async set(key, data, ctx) {
cache.set(key, {
value: data,
lastModified: Date.now(),
tags: ctx.tags,
})
}
async revalidateTag(tags) {
tags = [tags].flat()
for (let [key, value] of cache) {
if (value.tags.some((tag) => tags.includes(tag))) {
cache.delete(key)
}
}
}
resetRequestCache() {}
}
生产环境中,可以将缓存存储到 Redis 或 AWS S3 等持久化存储。
CDN 缓存策略
自动缓存行为
Next.js 自动设置 Cache-Control 头:
| 资源类型 | Cache-Control 头 |
|---|---|
| 不可变静态资源 | public, max-age=31536000, immutable |
| ISR 页面 | s-maxage: <revalidate>, stale-while-revalidate |
| 动态页面 | private, no-cache, no-store, max-age=0, must-revalidate |
静态资源 CDN 分离
如果要将静态资源托管到不同域名或 CDN,可以使用 assetPrefix 配置:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
assetPrefix: 'https://cdn.example.com',
}
export default nextConfig
Note分离静态资源到不同域名会增加 DNS 和 TLS 解析时间,需要权衡利弊。
功能支持矩阵
不同 Next.js 功能对平台能力的要求不同:
| 功能 | 流式传输 | 共享缓存 | 边缘拼接 |
|---|---|---|---|
| Server Components | 需要 | 不需要 | 不需要 |
| ISR(基于时间) | 不需要 | 推荐 | 不需要 |
| ISR(按需) | 不需要 | 推荐 | 不需要 |
| 部分预渲染 | 需要 | 推荐 | 可选 |
| 缓存组件 | 需要 | 推荐 | 不需要 |
| Proxy | 不需要 | 不需要 | 不需要 |
| Server Actions | 需要 | 不需要 | 不需要 |
Tip“流式传输需要”意味着平台必须支持分块传输编码或 HTTP/2 流式传输,不能缓冲响应。
版本倾斜处理
多实例或滚动部署时,可能出现版本倾斜问题:
- 资源缺失:客户端请求的 JS/CSS 文件在服务器上已不存在
- Server Actions 不匹配:客户端调用的 Server Action ID 来自旧版本
- 导航失败:预取的页面数据与新服务器不兼容
通过配置 deploymentId,Next.js 可以检测版本不匹配并触发硬导航:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
deploymentId: process.env.DEPLOYMENT_VERSION,
}
export default nextConfig
流式传输配置
使用 nginx 或类似代理时,需要禁用缓冲以支持流式传输:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/:path*{/}?',
headers: [
{
key: 'X-Accel-Buffering',
value: 'no',
},
],
},
]
},
}
export default nextConfig
确保整个基础设施端到端支持流式传输:
- 负载均衡器必须支持分块传输编码或 HTTP/2
- 反向代理必须透传分块响应
- 使用部分预渲染时,流式传输是必需的
小结
这一章我们学了各种部署方式:
- Vercel 部署最省心,集成体验最好
- Docker 用
output: 'standalone'减小镜像体积 - 多实例部署要统一加密密钥和共享缓存
- 配
deploymentId处理版本倾斜 - 用 CDN 时注意
Cache-Control头的设置