第二十一章:CI/CD 最佳实践
21.1 CI/CD 概览
核心流程
代码提交 → 测试 → 构建 → 部署 → 验证
主流平台
- GitHub Actions
- GitLab CI
- Jenkins
- CircleCI
- 阿里云效
21.2 GitHub Actions 配置
基础工作流
yaml
# .github/workflows/ci.yml
name: CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run type check
run: npm run type-check
- name: Run unit tests
run: npm run test:unit
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
部署到 GitHub Pages
yaml
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
VITE_BASE_URL: '/your-repo-name/'
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
部署到 Vercel
yaml
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Vercel CLI
run: npm install --global vercel
- name: Deploy to Vercel
run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
21.3 GitLab CI 配置
yaml
# .gitlab-ci.yml
image: node:18
stages:
- test
- build
- deploy
variables:
npm_config_cache: '$CI_PROJECT_DIR/.npm'
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
- node_modules/
.test_template: &test
stage: test
script:
- npm ci --cache .npm --prefer-offline
- npm run lint
- npm run type-check
- npm run test:unit
artifacts:
reports:
junit:
- junit.xml
paths:
- coverage/
unit-test:
<<: *test
build:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
only:
- main
- develop
deploy_staging:
stage: deploy
script:
- apt-get update -qq && apt-get install -y -qq sshpass
- sshpass -p $SSH_PASSWORD scp -r dist/* user@staging-server:/var/www/html/
environment:
name: staging
url: https://staging.example.com
only:
- develop
deploy_production:
stage: deploy
script:
- apt-get update -qq && apt-get install -y -qq sshpass
- sshpass -p $SSH_PASSWORD scp -r dist/* user@prod-server:/var/www/html/
environment:
name: production
url: https://example.com
only:
- main
when: manual
21.4 缓存策略优化
依赖缓存
yaml
# GitHub Actions 缓存配置
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
node_modules
~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Vite 构建缓存
javascript
// vite.config.js
export default defineConfig({
build: {
// 启用构建缓存
cacheDir: 'node_modules/.vite',
// 利用 CI 环境变量
sourcemap: process.env.CI === 'true'
}
})
并行构建
yaml
# 并行执行多个任务
jobs:
test:
strategy:
matrix:
task: [lint, type-check, test:unit, test:e2e]
steps:
- name: Run ${{ matrix.task }}
run: npm run ${{ matrix.task }}
21.5 环境变量管理
Secrets 配置
yaml
# GitHub Actions
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
# GitLab CI
variables:
VITE_API_URL: $VITE_API_URL
VITE_SENTRY_DSN: $VITE_SENTRY_DSN
多环境配置
javascript
// 根据 CI 环境变量动态加载
const env = process.env.CI_ENVIRONMENT_NAME || 'development'
const envConfig = {
development: {
VITE_API_URL: 'http://localhost:3000'
},
staging: {
VITE_API_URL: 'https://staging-api.example.com'
},
production: {
VITE_API_URL: 'https://api.example.com'
}
}
21.6 测试优化
并行测试
yaml
# 使用 vitest 并行测试
- name: Run tests
run: npm run test:unit -- --reporter=default --run --pool=forks
E2E 测试
yaml
# Playwright E2E 测试
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Build for E2E
run: npm run build
- name: Run E2E tests
run: npm run test:e2e
测试报告
yaml
# 上传测试报告
- name: Upload test results
uses: actions/upload-artifact@v3
with:
name: test-results
path: test-results/
21.7 构建优化
增量构建
javascript
// 利用缓存加速构建
build: {
// 只在依赖变化时重新构建
rollupOptions: {
cache: true
}
}
构建大小限制
yaml
# 在 CI 中检查构建大小
- name: Check bundle size
run: |
SIZE=$(du -sk dist | cut -f1)
LIMIT=500
if [ $SIZE -gt $LIMIT ]; then
echo "❌ Bundle size $SIZE KB exceeds limit $LIMIT KB"
exit 1
fi
echo "✅ Bundle size $SIZE KB within limit"
21.8 部署策略
蓝绿部署
yaml
# 示例:切换负载均衡器
- name: Deploy to Blue
run: rsync -avz dist/ blue-server:/var/www/html/
- name: Switch to Blue
run: |
ssh load-balancer "sudo update-lb --set blue"
sleep 30
ssh load-balancer "sudo update-lb --health-check"
灰度发布
yaml
- name: Canary deployment
run: |
# 部署到 10% 的实例
for i in {1..10}; do
rsync -avz dist/ canary-$i:/var/www/html/
done
# 等待验证
sleep 300
# 全量部署
for i in {11..100}; do
rsync -avz dist/ server-$i:/var/www/html/
done
回滚机制
yaml
# 保留历史版本
- name: Archive previous version
run: |
tar -czf /backups/vite-app-$(date +%Y%m%d-%H%M%S).tar.gz dist/
- name: Deploy new version
run: rsync -avz dist/ /var/www/html/
# 回滚脚本
rollback:
script:
- LATEST_BACKUP=$(ls -t /backups/vite-app-*.tar.gz | head -1)
- tar -xzf $LATEST_BACKUP -C /var/www/html/
21.9 监控与告警
健康检查
yaml
- name: Health check
run: |
curl -f https://example.com/health || exit 1
性能监控集成
yaml
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun --upload.target=temporary-public-storage
21.10 最佳实践总结
优化清单
- 依赖管理 :使用
npm ci而非npm install - 缓存策略:缓存 node_modules 和构建产物
- 并行执行:独立任务并行运行
- 环境隔离:不同环境使用不同配置
- 安全保密:敏感信息使用 Secrets
- 构建验证:检查构建产物大小和质量
- 自动化测试:单元测试 + E2E 测试
- 部署策略:蓝绿部署或灰度发布
- 回滚能力:保留历史版本,支持快速回滚
- 监控告警:部署后健康检查和性能监控
常见问题
Q: CI 中构建慢怎么办?
A: 启用缓存、使用并行任务、升级 CI 机器配置
Q: 环境变量如何管理?
A: 使用 CI 平台的 Secrets 功能,不同环境单独配置
Q: 部署失败如何快速回滚?
A: 保留至少 3 个历史版本,准备回滚脚本
Q: 如何确保构建产物一致性?
A: 锁定依赖版本(package-lock.json),使用固定 Node 版本
本章小结
CI/CD 是现代前端工程的基石:
- 自动化:减少人工操作,提升效率
- 一致性:确保构建环境一致
- 可靠性:测试自动化,降低风险
- 可追溯:每次部署都有记录
根据团队规模和项目复杂度,选择合适的 CI/CD 策略。