React: 部署与 CI/CD
最后更新:2026-08-26
Tom 的博客在本地跑得好好的,但部署到线上后遇到了各种问题:图片加载不出来、API 地址写死成 localhost、每次手动 SSH 上传代码又慢又容易出错。他需要一套自动化的部署流水线,让代码从 push 到上线变成全自动流程。
本课将带你走完从"本地开发"到"生产上线"的完整路径。你将学会使用 Vercel 一键部署、配置 GitHub Actions 自动化流水线、管理多环境变量,以及优化构建产物以提升页面加载速度。这些部署技能是前端工程师从"会写代码"到"会交付产品"的关键一步。
1. 你将学到
- Vercel 自动部署(Git 集成、域名配置、团队协作)
- Netlify / Docker / 传统服务器多种部署方案对比
- GitHub Actions CI/CD 流水线搭建
- 环境变量管理(开发/预览/生产三环境隔离)
- 构建优化策略(包体积分析、CDN、压缩、缓存)
@next/bundle-analyzer可视化分析包体积的具体方法next/image组件自动优化图片加载的原理与配置
2. 概念图解
Tom 设计了一条完整的 CI/CD 流水线:开发者 push 代码到 GitHub 后,自动触发构建和测试流程,通过后自动部署到预览环境供审核,审核通过合并到主分支后自动部署到生产环境。
流水线的核心是自动化和标准化。所有检查步骤(lint、type-check、test)都在 CI 环境中自动执行,不需要人工干预。这样任何代码质量问题都能在合并前被发现,确保生产环境只部署经过验证的代码。Vercel 的 Preview Deployment 会自动为每个分支生成独立的预览 URL,方便产品经理和测试人员直接点击查看效果。
flowchart LR
A[开发者 Push 代码] --> B[GitHub 接收]
B --> C[GitHub Actions 触发]
C --> D{运行 CI 流程}
D --> E[安装依赖<br/>npm ci]
E --> F[代码检查<br/>lint / type-check]
F --> G[运行测试<br/>jest / playwright]
G --> H[构建<br/>next build]
H --> I{部署目标}
I -->|Feature 分支| J[Vercel Preview<br/>预览环境]
I -->|Main 分支| K[Vercel Production<br/>生产环境]
J --> L[Preview URL 自动生成]
K --> M[CDN 分发<br/>全球加速]
3. 一个真实场景
| 部署方案 | 构建方式 | ISR 支持 | 运维成本 | 适用场景 |
|---|---|---|---|---|
| Vercel | 自动(Git push) | ✅ 原生 | 极低 | Next.js 项目、个人/小团队 |
| Netlify | 自动(Git push) | ⚠️ 需配置 | 低 | 静态站点、Gatsby |
| Docker + Nginx | 手动/CI | ❌ 需自建 | 中 | 私有部署、需要完全控制 |
| AWS Amplify | 自动 | ⚠️ 有限 | 中 | AWS 生态项目 |
| 传统服务器 PM2 | 手动 | ❌ | 高 | 复杂定制需求 |
Tom 的博客部署问题清单越来越长:每次更新文章都要 SSH 登录服务器、手动运行 git pull、npm run build、pm2 restart,整个过程至少 10 分钟。如果忘了备份数据库,一失手就全没了。更头疼的是,团队其他成员的修改经常覆盖他的代码。
他决定采用 Vercel + GitHub Actions 的现代部署方案。第一步将代码从 FTP 迁移到 GitHub 仓库;第二步连接 Vercel 实现自动部署;第三步配置 GitHub Actions 添加代码检查和测试流程。改造完成后,每次 push 代码到 main 分支,Vercel 自动构建部署,整个流程不到 2 分钟。
Tom 还对比了几种部署方案的优劣:Vercel 适合 Next.js 项目,配置最简单;Netlify 也支持 Next.js 但部分高级特性(ISR、Middleware)需要额外配置;Docker 部署适合需要完全控制服务器环境的场景;传统服务器部署(Nginx + PM2)灵活度最高但运维成本最大。对于个人博客和小型项目,Vercel 是毫无疑问的最佳选择。
(1) Vercel 自动部署
Vercel 是 Next.js 的创建者 Vercel 公司提供的 Serverless 部署平台。它与 Next.js 深度集成,支持自动检测框架、Serverless Functions、Edge Functions、ISR、Middleware 等所有 Next.js 特性。Vercel 的自动部署机制基于 Git 集成——连接 GitHub/GitLab/Bitbucket 仓库后,每次 push 自动触发构建和部署。
Vercel 提供三个环境:Production(生产环境,绑定自定义域名)、Preview(预览环境,每个分支自动生成独立 URL)、Development(本地开发环境)。Preview 环境特别适合团队协作——每次 PR 自动生成预览 URL,方便 Reviewer 在真实环境中查看效果。
Vercel 部署步骤(GUI 方式)
- 在 Vercel Dashboard 点击
Add New -> Project - 选择 GitHub 仓库并授权 Vercel 访问
- Vercel 自动检测框架为 Next.js,使用默认配置
- 在 Environment Variables 中添加必要的环境变量
- 点击 Deploy,等待约 1-2 分钟完成部署
- 部署完成后,Vercel 自动生成
.vercel.app域名 - 在 Settings -> Domains 中添加自定义域名
Vercel 部署步骤(CLI 方式)
# 1. 全局安装 Vercel CLI
npm install -g vercel
# 2. 登录 Vercel 账号
vercel login
# 3. 在项目根目录执行部署
# 首次运行会引导配置项目设置
vercel
# 4. 部署到生产环境
vercel --prod
▶ 示例 1:Vercel 部署配置与 CLI
# 1. 安装 Vercel CLI
npm install -g vercel
# 2. 项目根目录登录
vercel login
# 3. 项目根目录部署到预览环境
vercel
# 4. 部署到生产环境
vercel --prod
# 5. 查看当前部署状态
vercel ls
# 6. 查看部署日志
vercel logs --all
// next.config.ts - Vercel 自动读取此配置
import type { NextConfig } from 'next'
const config: NextConfig = {
// 图片优化配置 - 允许远程图片域名
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
{ protocol: 'https', hostname: '**.cloudfront.net' },
],
},
// HTTP 压缩
compress: true,
// 移除 X-Powered-By 头(安全考虑)
poweredByHeader: false,
// 自定义构建目录(可选)
distDir: '.next',
// 启用严格模式
reactStrictMode: true,
}
export default config
// vercel.json - Vercel 项目配置(可选,大多数项目不需要)
{
"framework": "nextjs",
"buildCommand": "npm run build",
"outputDirectory": ".next",
"regions": ["hnd1", "iad1"],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
]
}
(2) GitHub Actions CI/CD
GitHub Actions 是 GitHub 提供的 CI/CD 服务,通过 YAML 配置文件定义工作流。每次 push 或 PR 时自动触发指定的 job 序列。Tom 配置了三个 job:第一个在 push 到任何分支时运行 lint 和类型检查;第二个在 push 到 main 分支时运行全量测试和构建;第三个在发布新版本时自动部署到预览环境。
工作流文件放在 .github/workflows/ 目录下,文件名自定义。每个工作流可以包含多个 job,job 之间可以设置依赖关系。GitHub 提供了丰富的 marketplace action,可以直接复用社区构建好的步骤。
GitHub Actions 的核心概念包括:on 定义触发事件(push、pull_request、schedule 等)、jobs 定义要执行的任务、steps 定义每个任务的执行步骤、actions/ 是社区贡献的可复用模块。Tom 的工作流中使用了 actions/checkout@v4(检出代码)、actions/setup-node@v4(配置 Node.js 环境)、actions/upload-artifact@v4(上传构建产物)三个社区 action。
▶ 示例 2:完整的 GitHub Actions 工作流
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# Job 1:代码质量和类型检查
quality:
name: Code Quality Check
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: TypeScript type check
run: npx tsc --noEmit
- name: Lint check
run: npm run lint
# Job 2:运行测试 + 构建
test-and-build:
name: Test & Build
needs: quality # 依赖 quality 任务成功
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: Run unit tests
run: npm test
env:
CI: true
- name: Build project
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: next-build
path: .next/
# Job 3:自动部署到 Vercel
deploy:
name: Deploy to Vercel
needs: test-and-build
runs-on: ubuntu-latest
# 只在 main 分支部署
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
(3) 构建优化策略
部署不仅仅是将代码上传到服务器。一个优化良好的构建配置可以大幅提升页面加载速度、降低带宽成本。Tom 在部署优化上投入了不少精力,主要优化了三个方面:包体积分析、图片优化、缓存策略。
包体积分析使用 @next/bundle-analyzer 插件,可以可视化地查看每个模块的大小,找出体积异常的依赖。Tom 发现他的项目中 moment.js 占了 85KB,换成 dayjs(6KB)后首屏 JS 减少了 28%。他还发现一个从未使用的组件被全局导入,通过 Tree Shaking 移除了它。
图片优化通过 Next.js 内置的 next/image 组件实现,自动生成 WebP/AVIF 格式、响应式尺寸、懒加载。Tom 的博客文章中有大量图片,使用 next/image 后,图片加载时间从平均 1.2 秒降到 0.3 秒。
缓存策略通过配置 CDN 的 Cache-Control 头实现。静态资源(JS、CSS、图片)设置一年缓存(max-age=31536000),HTML 页面设置较短缓存(max-age=60)配合 ISR 自动更新。这样用户第一次访问后,后续的静态资源直接从浏览器缓存加载,无需重新请求。
其他部署方案速查
| 方案 | 配置复杂度 | Next.js 兼容性 | 适用场景 |
|---|---|---|---|
| Vercel | 极低 | 完美 | Next.js 首选部署平台 |
| Netlify | 低 | 良好(部分高级特性受限) | 小型静态站点 |
| Docker | 中 | 良好 | 需要环境一致性的团队项目 |
| 传统 Nginx | 高 | 需要手动配置 SSR | 企业自建服务器 |
| AWS Amplify | 中 | 良好 | AWS 生态项目 |
▶ 示例 3:构建优化配置
// next.config.ts - 完整优化配置
import type { NextConfig } from 'next'
// 包体积分析(按需启用)
const withBundleAnalyzer = process.env.ANALYZE === 'true'
? require('@next/bundle-analyzer')({ enabled: true })
: (config: any) => config
const config: NextConfig = {
// === 图片优化 ===
images: {
// 允许的远程图片域名
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
{ protocol: 'https', hostname: 'cdn.example.com' },
],
// 图片格式(默认已支持 WebP)
formats: ['image/avif', 'image/webp'],
// 设备断点(根据设计稿配置)
deviceSizes: [640, 768, 1024, 1280, 1536],
},
// === 安全与性能 ===
compress: true,
poweredByHeader: false,
reactStrictMode: true,
// === CDN 配置 ===
// 如果使用自定义 CDN,设置 assetPrefix
// assetPrefix: 'https://cdn.example.com',
// === 实验性功能 ===
experimental: {
// 优化 CSS 体积
optimizePackageImports: ['antd', '@ant-design/icons', 'lodash-es'],
},
}
export default withBundleAnalyzer(config)
// vercel.json - 缓存与安全头配置
// {
// "headers": [
// {
// "source": "/static/(.*)",
// "headers": [
// { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
// ]
// },
// {
// "source": "/_next/image(.*)",
// "headers": [
// { "key": "Cache-Control", "value": "public, max-age=86400, stale-while-revalidate=2592000" }
// ]
// }
// ]
// }
# 包体积分析命令
ANALYZE=true npm run build
# 该命令会生成两个 HTML 报告:
# - .next/analyze/client.html (客户端代码分析)
# - .next/analyze/server.html (服务端代码分析)
# 打开浏览器查看报告后,可以找出体积过大的依赖并优化
# 常见优化手段:
# 1. 动态导入: const HeavyComponent = dynamic(() => import('./HeavyComponent'))
# 2. 替换大体积库: moment → dayjs(缩小 95%)
# 3. Tree Shaking: import { Button } from 'antd' 代替 import { Button } from 'antd/es/button'
优化前后对比示例
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 首屏 JS 体积 | 285 KB | 168 KB | 减少 41% |
| Lighthouse 性能分 | 62 | 94 | 提升 32 分 |
| TTFB(首字节时间) | 420ms | 180ms | 减少 57% |
| 构建时间 | 3m 12s | 1m 45s | 减少 45% |
| CDN 命中率 | 52% | 95% | 提升 43% |
▶ 示例 4:GitHub Actions CI 流水线配置
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test -- --coverage
- name: Upload coverage
if: matrix.node-version == 20
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
build:
needs: lint-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Check bundle size
run: |
SIZE=$(du -sk .next/static | cut -f1)
echo "Bundle size: ${SIZE}KB"
if [ "$SIZE" -gt 500 ]; then
echo "⚠️ Bundle exceeds 500KB threshold"
fi
▶ 示例 5:综合——Next.js 生产部署完整配置
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Type check
run: npx tsc --noEmit
- name: Run tests
run: npm test
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ vars.NEXT_PUBLIC_API_URL }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Run Lighthouse audit
uses: treosh/lighthouse-ci-action@v12
with:
urls: |
http://localhost:3000
uploadArtifacts: true
budgetPath: ./lighthouse-budget.json
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
working-directory: ./
- name: Notify deployment
if: always()
run: |
STATUS="${{ job.status }}"
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H 'Content-type: application/json' \
-d "{\"text\":\"Deploy $STATUS on $(date -u +%Y-%m-%dT%H:%MZ)\"}"
// lighthouse-budget.json
[
{
"path": "/*",
"options": { "first-contentful-paint": { "maxNumericValue": 2000 } },
"budgets": [
{ "resourceSizes": [{ "resourceType": "script", "budget": 200 }, { "resourceType": "stylesheet", "budget": 50 }, { "resourceType": "image", "budget": 300 }, { "resourceType": "total", "budget": 600 }] }
]
}
]
❓ 常见问题
NEXT_PUBLIC_ 前缀的变量会被打包到浏览器端 JS 中,无前缀的变量只在服务端可用。在 GitHub Actions 中通过 secrets 传递敏感变量,工作流中通过 ${{ secrets.XXX }} 引用。npm test 确认通过后再 push。也可以配置 continue-on-error: true 让某些 Job 失败时继续执行。@next/bundle-analyzer 分析每个依赖的打包体积,找出可以替换或动态导入的大体积库。图片优化通常是最容易见效的优化点——使用 next/image 自动生成 WebP 格式和响应式尺寸。📖 小节
- Vercel 是 Next.js 的最佳部署平台,支持 Git 集成自动部署、Preview 环境、Serverless Functions、全球 CDN
- GitHub Actions 工作流定义在
.github/workflows/*.yml中,支持多 Job 编排和依赖关系(needs控制执行顺序) - 标准 CI/CD 流程:代码检查 -> 类型检查 -> 运行测试 -> 构建 -> 部署(各阶段可并行或串行)
- Vercel 环境变量按 Production/Preview/Development 三环境隔离,敏感变量通过 Dashboard 或 CLI 设置
- 构建优化三件套:包体积分析(
@next/bundle-analyzer)、图片优化(next/image)、CDN 缓存(Cache-Control) dynamic动态导入和optimizePackageImports可以有效减少首屏 JS 体积- Preview 部署让每次 PR 都有独立 URL,方便团队协作审查
next.config.ts中compress、poweredByHeader、images等配置影响安全性和性能- 部署方案选择:Vercel 最适合 Next.js,Netlify 适合静态站点,Docker 适合团队项目,传统部署适合企业场景
- CI/CD 核心价值:自动化代码质量检查,确保生产环境只部署经过验证的代码
- 环境变量按前缀区分作用域:无前缀(服务端)、
NEXT_PUBLIC_(浏览器端)、NEXT_PRIVATE_(服务端显式声明) next/image组件自动优化图片加载:WebP/AVIF 格式转换、响应式尺寸、懒加载、CDN 缓存- TTFB、Lighthouse 评分、首屏 JS 体积是衡量部署质量的核心指标
📝 作业
- 将你的 Next.js 项目推送到 GitHub 仓库,然后在 Vercel 中导入该项目(Import Git Repository)。观察自动部署流程,验证部署后的 Preview URL 和 Production URL 是否正确。在 Vercel Dashboard 中查看部署日志,理解构建过程的每个步骤。配置自定义域名(可选)并开启 HTTPS。
- 在项目中创建
.github/workflows/ci.yml,配置一个 CI 工作流:push 到任意分支时自动运行npm ci->npm run lint->npm test->npm run build。故意引入一个 lint 错误或测试失败,push 后观察 CI 是否失败并显示错误信息,然后修复错误确认 CI 通过。 - 为项目配置构建优化:使用
@next/bundle-analyzer分析当前项目的包体积,找出体积最大的前 3 个依赖。对其中一个大体积组件实现动态导入(dynamic(() => import(...))),对比优化前后的 JS 体积变化。在next.config.ts中启用图片优化(配置remotePatterns)和压缩配置。最后用 Chrome DevTools 的 Lighthouse 测试优化前后的性能分数,记录首屏 JS 体积和性能评分的提升幅度。