前端工程化三部曲:CI/CD 与自动化发布
代码写完只是开始,自动构建、测试、部署才是工程化的终点。这篇带你打通从 Git 提交到线上发布的完整流水线。
一、导读
这篇解决什么问题
当你遇到以下情况时,这篇文章能帮你建立系统化的解决方案:
- 每次发版手动打包、上传、改配置,耗时耗力还容易出错
- 团队成员代码风格不统一,Code Review 时还在争论分号问题
- 测试环境部署频繁,但总是漏掉某个步骤导致环境不一致
- 生产环境出问题了,却不知道当前线上跑的是哪次提交的代码
- 想引入自动化测试,但不知道在 CI 流程中放在哪个环节
本文专注于 前端 CI/CD 的完整流水线设计,从 Git 工作流、自动化检查、构建部署到发布策略,带你从「手动发布」升级到「一键上线」。
二、CI/CD 核心概念
1 什么是 CI/CD
┌─────────────────────────────────────────────────────────┐
│ CI/CD 流水线全景 │
├─────────────────────────────────────────────────────────┤
│ │
│ 开发者推送代码 │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ CI(持续集成) │ │
│ │ • 代码拉取 → 依赖安装 → 代码检查 → 单元测试 │ │
│ │ • 构建打包 → 产物上传 → 触发通知 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ↓ 构建成功 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ CD(持续部署/交付) │ │
│ │ • 部署到测试环境 → 集成测试 → 人工验收 │ │
│ │ • 部署到预发布环境 → 灰度发布 → 全量发布 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ 线上环境自动更新 │
│ │
└─────────────────────────────────────────────────────────┘
CI(Continuous Integration):持续集成,强调频繁地将代码合并到主干,并通过自动化验证确保合并质量。
CD 有两种理解:
- Continuous Delivery(持续交付):代码自动构建、测试,但发布到生产环境需要人工审批
- Continuous Deployment(持续部署):代码通过所有检查后自动发布到生产环境,无需人工干预
2 前端 CI/CD 的独特性
| 特性 | 前端 | 后端 |
|---|---|---|
| 构建产物 | 静态文件(HTML/CSS/JS) | 可执行程序/容器镜像 |
| 部署目标 | CDN / 静态服务器 / OSS | 应用服务器 / K8s |
| 缓存策略 | 文件名哈希 + 长期缓存 | 通常不依赖客户端缓存 |
| 环境差异 | 构建时注入环境变量 | 运行时读取配置 |
| 回滚方式 | 切换 CDN 路径或回退版本 | 重启服务或切换容器 |
三、Git 工作流:CI/CD 的触发源头
1 主流分支模型对比
┌─────────────────────────────────────────────────────────┐
│ Git Flow(适合版本化发布) │
├─────────────────────────────────────────────────────────┤
│ │
│ main ─────●────────●────────●────────●─────► │
│ 1.0 1.1 1.2 1.3 │
│ ↑ ↑ ↑ ↑ │
│ develop ──●──●──●──●──●──●──●──●──●──●──► │
│ ↘ ↗ ↘ ↗ │
│ feature/a feature/b hotfix/1.2.1 │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ GitHub Flow(适合持续部署) │
├─────────────────────────────────────────────────────────┤
│ │
│ main ─────●────────●────────●────────●─────► │
│ ↑ ↑ ↑ ↑ │
│ feature/a feature/b feature/c feature/d │
│ (PR → Review → Merge → Auto Deploy) │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Trunk-Based(适合高频发布) │
├─────────────────────────────────────────────────────────┤
│ │
│ main ─────●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─●─► │
│ (所有提交直接进主干,用 Feature Flag 控制功能开关) │
│ │
└─────────────────────────────────────────────────────────┘
选型建议:
- Git Flow:有明确版本号的产品(如桌面应用、需要版本回退的 B 端系统)
- GitHub Flow:Web 应用、SaaS 产品、追求快速迭代
- Trunk-Based:大型团队、每日多次发布、配合 Feature Flag 使用
2 提交规范:自动化的基础
<type>(<scope>): <subject>
<body>
<footer>
常用 type:
| 类型 | 含义 | 是否触发版本升级 |
|---|---|---|
feat |
新功能 | minor |
fix |
修复 bug | patch |
docs |
文档更新 | 否 |
style |
代码格式(不影响功能) | 否 |
refactor |
重构 | patch |
perf |
性能优化 | patch |
test |
测试相关 | 否 |
chore |
构建/工具链变动 | 否 |
示例:
bash
feat(auth): 添加 OAuth2.0 登录支持
- 支持 GitHub、Google 第三方登录
- 添加登录状态持久化
- 更新相关单元测试
Closes #123
3 自动化版本管理
javascript
// package.json
{
"version": "1.2.3",
// 主版本.次版本.修订号
// 1.0.0 → 1.1.0 (feat)
// 1.1.0 → 1.1.1 (fix)
// 1.1.1 → 2.0.0 (BREAKING CHANGE)
}
工具推荐:
- standard-version:基于 Angular 提交规范自动生成 CHANGELOG 和版本号
- semantic-release:全自动版本管理和发布,与 CI 深度集成
四、CI 流水线设计
1 流水线阶段划分
┌─────────────────────────────────────────────────────────┐
│ 前端 CI 流水线 │
├─────────────────────────────────────────────────────────┤
│ │
│ Stage 1: 环境准备 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 检出代码 │ │
│ │ • 设置 Node.js 版本 │ │
│ │ • 安装依赖(利用缓存加速) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 2: 代码质量检查 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • ESLint / Prettier 检查 │ │
│ │ • TypeScript 类型检查 │ │
│ │ • 提交信息规范检查(commitlint) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 3: 测试 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 单元测试(Vitest / Jest) │ │
│ │ • 组件测试(Vue Test Utils / React Testing Lib)│ │
│ │ • E2E 测试(Playwright / Cypress) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 4: 构建 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 生产环境构建 │ │
│ │ • 产物分析(包体积检查) │ │
│ │ • Source Map 生成(按需) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ Stage 5: 产物处理 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 上传构建产物到存储(OSS / S3) │ │
│ │ • 生成部署配置 │ │
│ │ • 触发部署流水线 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
2 依赖安装优化
yaml
# GitHub Actions 示例:缓存 pnpm 依赖
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm' # 自动缓存 pnpm store
- name: Install dependencies
run: pnpm install --frozen-lockfile
关键优化点:
- 使用
--frozen-lockfile确保依赖版本一致性 - 利用 CI 平台缓存机制加速
node_modules恢复 - 考虑使用
pnpm的依赖复用能力减少安装时间
3 代码检查并行化
yaml
# 并行执行独立检查任务
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
- run: pnpm install
- run: pnpm lint # ESLint + Prettier
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
- run: pnpm install
- run: pnpm type-check # tsc --noEmit
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
- run: pnpm install
- run: pnpm test # 单元测试
4 构建产物分析
javascript
// vite.config.ts - 集成 rollup-plugin-visualizer
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
// ...其他插件
process.env.ANALYZE && visualizer({
open: false,
gzipSize: true,
brotliSize: true,
filename: 'dist/stats.html'
})
]
});
构建检查清单:
- 包体积是否超过阈值(如首屏 JS > 200KB)
- 是否有重复依赖(lodash vs lodash-es)
- Source Map 是否正确生成
- 环境变量是否正确注入
五、GitHub Actions 实战
1 完整工作流示例
yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# 阶段 1:代码质量检查
quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm lint
- name: Type check
run: pnpm type-check
# 阶段 2:测试
test:
runs-on: ubuntu-latest
needs: quality # 质量检查通过后执行
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test:unit
- run: pnpm test:e2e
# 阶段 3:构建
build:
runs-on: ubuntu-latest
needs: [quality, test]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
# 阶段 4:部署到测试环境(develop 分支)
deploy-staging:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: dist
path: dist/
- name: Deploy to staging
run: |
# 上传到测试环境 CDN 或服务器
aws s3 sync dist/ s3://staging-bucket/
2 环境变量管理
yaml
# 不同环境使用不同变量
jobs:
build:
steps:
- name: Build for staging
if: github.ref == 'refs/heads/develop'
run: pnpm build
env:
VITE_API_URL: https://api-staging.example.com
VITE_APP_ENV: staging
- name: Build for production
if: github.ref == 'refs/heads/main'
run: pnpm build
env:
VITE_API_URL: https://api.example.com
VITE_APP_ENV: production
安全注意事项:
- 敏感信息使用 GitHub Secrets:
${``{ secrets.API_KEY }} - 避免在代码中硬密钥
- 生产环境变量与测试环境隔离
3 条件执行与矩阵构建
yaml
# 矩阵构建:多 Node 版本、多操作系统测试
jobs:
test:
strategy:
matrix:
node-version: [16, 18, 20]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
# ...测试步骤
六、Docker 容器化部署
1 前端项目的 Dockerfile
dockerfile
# 多阶段构建:减小最终镜像体积
# 阶段 1:构建
FROM node:18-alpine AS builder
WORKDIR /app
# 先复制依赖文件,利用缓存层
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
# 复制源码并构建
COPY . .
RUN pnpm build
# 阶段 2:运行(使用 Nginx 托管静态文件)
FROM nginx:alpine
# 复制构建产物到 Nginx 目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制自定义 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
2 Nginx 配置优化
nginx
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 静态资源长期缓存(因为文件名有哈希)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 文件不缓存(确保总是获取最新版本)
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache";
}
# 前端路由支持(单页应用)
location / {
try_files $uri $uri/ /index.html;
}
}
3 Docker 构建与推送
yaml
# GitHub Actions 中构建并推送 Docker 镜像
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/app:${{ github.sha }}
${{ secrets.DOCKER_USERNAME }}/app:latest
cache-from: type=gha
cache-to: type=gha,mode=max
七、部署策略
1 部署方式对比
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| CDN 部署 | 纯静态站点 | 全球加速、成本低 | 需要处理缓存刷新 |
| 对象存储 | 静态资源托管 | 高可用、自动扩容 | 需要配合 CDN 使用 |
| Docker + K8s | 微服务架构 | 环境一致、易于回滚 | 运维成本高 |
| Serverless | 低频访问站点 | 按需付费、免运维 | 冷启动延迟 |
2 蓝绿部署与滚动部署
┌─────────────────────────────────────────────────────────┐
│ 蓝绿部署 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户流量 ──────┐ │
│ ↓ │
│ ┌─────────┐ │
│ │ 负载均衡 │ │
│ └────┬────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ ↓ ↓ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 蓝环境 │ │ 绿环境 │ │
│ │ (v1.0) │ │ (v2.0) │ │
│ │ 运行中 │ │ 待切换 │ │
│ └─────────┘ └─────────┘ │
│ │
│ 切换时:负载均衡瞬间指向绿环境,蓝环境保留用于回滚 │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 滚动部署 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户流量 ───────────────────────────────► │
│ │
│ 实例 1: [v1.0] → [v2.0] │
│ 实例 2: [v1.0] → [v1.0] → [v2.0] │
│ 实例 3: [v1.0] → [v1.0] → [v1.0] → [v2.0] │
│ │
│ 特点:逐步替换实例,期间新旧版本共存 │
│ │
└─────────────────────────────────────────────────────────┘
3 前端灰度发布实践
javascript
// 基于用户 ID 的灰度策略
function isGrayRelease(userId, grayPercent = 10) {
// 将用户 ID 哈希为数字,取模判断是否命中灰度
const hash = userId.split('').reduce((acc, char) => {
return acc + char.charCodeAt(0);
}, 0);
return hash % 100 < grayPercent;
}
// 应用加载时决定使用哪个版本
const scriptSrc = isGrayRelease(currentUser.id, 20)
? 'https://cdn.example.com/app/v2.0/main.js'
: 'https://cdn.example.com/app/v1.0/main.js';
八、自动化测试集成
1 测试金字塔在前端的应用
┌─────────┐
│ E2E │ ← 用户视角,覆盖核心流程
│ 少量 │ Playwright / Cypress
├─────────┤
│ 集成 │ ← 组件组合测试
│ 中等 │ Vue Test Utils / React Testing Lib
├─────────┤
│ 单元 │ ← 函数/工具类测试
│ 大量 │ Vitest / Jest
└─────────┘
2 CI 中的测试策略
yaml
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: pnpm install
- run: pnpm test:unit --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
e2e-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: pnpm install
- run: pnpm build
- name: Run E2E tests
run: pnpm test:e2e
env:
CI: true
3 视觉回归测试
yaml
# 使用 Chromatic 或 Percy 进行 UI 回归测试
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
九、监控与回滚
1 部署后健康检查
yaml
jobs:
deploy:
steps:
- name: Deploy
run: ./deploy.sh
- name: Health check
run: |
for i in {1..5}; do
curl -f https://app.example.com/health && exit 0
sleep 10
done
exit 1
2 错误监控集成
javascript
// 生产环境集成 Sentry
import * as Sentry from '@sentry/vue';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.VITE_APP_ENV,
release: import.meta.env.VITE_APP_VERSION, // 关联版本号
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration()
]
});
3 快速回滚策略
bash
# 基于 Docker 镜像的回滚
# 1. 查看历史版本
docker images | grep my-app
# 2. 快速切换到上一个版本
docker stop app-current
docker run -d --name app-rollback -p 80:80 my-app:previous-tag
# 基于 CDN 的回滚
# 1. 将 CDN 回源路径指向上一个版本目录
# 2. 刷新 CDN 缓存
十、完整流水线示例
一个现代化的前端 CI/CD 配置
yaml
# .github/workflows/main.yml
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: 18
jobs:
# ========== 质量门禁 ==========
lint-and-type:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm type-check
# ========== 测试 ==========
test:
name: Test
runs-on: ubuntu-latest
needs: lint-and-type
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test:unit --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
# ========== 构建 ==========
build:
name: Build
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Analyze bundle size
run: pnpm analyze
- name: Upload dist
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
# ========== 部署到预发布环境 ==========
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.example.com
steps:
- name: Download dist
uses: actions/download-artifact@v3
with:
name: dist
- name: Deploy to S3
run: aws s3 sync . s3://staging-bucket/ --delete
- name: Invalidate CDN
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DIST_ID }} --paths "/*"
# ========== 部署到生产环境 ==========
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- name: Download dist
uses: actions/download-artifact@v3
with:
name: dist
- name: Deploy to S3
run: aws s3 sync . s3://production-bucket/ --delete
- name: Invalidate CDN
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_PROD_DIST_ID }} --paths "/*"
- name: Notify Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ 生产环境部署成功: ${{ github.sha }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
十一、最佳实践总结
CI/CD checklist
- Git 规范:提交信息规范、分支保护、Code Review 强制要求
- 自动化检查:Lint、Type Check、单元测试必须在合并前通过
- 构建可复现:lock 文件提交、Node 版本锁定、依赖缓存
- 环境隔离:开发/测试/生产环境配置分离,敏感信息用 Secrets
- 产物管理:构建产物版本化、Source Map 按需生成
- 部署策略:蓝绿部署或灰度发布,支持快速回滚
- 监控告警:部署后健康检查、错误监控、性能监控
- 文档化:流水线配置、环境变量说明、回滚操作手册
常见陷阱
| 陷阱 | 后果 | 解决方案 |
|---|---|---|
| 不锁依赖版本 | 构建结果不一致 | 使用 lock 文件 + --frozen-lockfile |
| 在 CI 中运行开发服务器 | 流程卡住 | 只执行构建命令,不启动 dev server |
| 忽略 CI 缓存 | 每次全量安装依赖 | 配置 actions/cache 或 setup-node 的 cache |
| 生产环境构建无 Source Map | 线上错误无法定位 | 生成 hidden-source-map,不上传至服务器 |
| 环境变量硬编码 | 安全风险 | 使用 CI Secrets + 构建时注入 |
| 缺少回滚方案 | 故障恢复慢 | 保留历史版本、准备快速回滚脚本 |
十二、延伸阅读与工具
| 工具/平台 | 用途 |
|---|---|
| GitHub Actions | CI/CD 工作流编排 |
| GitLab CI | 替代方案,与 GitLab 深度集成 |
| CircleCI | 云端 CI 服务,配置简洁 |
| Jenkins | 自托管 CI 服务器,高度可定制 |
| Docker | 容器化打包与部署 |
| Kubernetes | 容器编排,适合大规模部署 |
| Vercel / Netlify | 前端托管平台,零配置 CI/CD |
| Sentry | 错误监控与性能追踪 |
| Codecov | 测试覆盖率报告 |
| semantic-release | 自动化版本管理与发布 |
十三、前端安全:CI/CD 中的安全左移
1 依赖安全检查
bash
# 使用 npm audit 检查已知漏洞
npm audit
# 使用 Snyk 进行深度扫描
npx snyk test
# 在 CI 中阻断高危漏洞
npm audit --audit-level=high
CI 集成:
yaml
- name: Security audit
run: |
pnpm audit --audit-level high
if [ $? -ne 0 ]; then
echo "发现高危安全漏洞,请修复后重试"
exit 1
fi
2 代码扫描与 Secret 检测
yaml
# 使用 GitLeaks 检测代码中的密钥泄露
- name: Detect secrets
uses: zricethezav/gitleaks-action@master
with:
config-path: .gitleaks.toml
常见泄露场景:
- 硬编码的 API Key、数据库密码
.env文件误提交到仓库- 测试代码中的真实凭证
3 供应链安全
bash
# 锁定依赖版本,防止恶意更新
# package.json 中使用精确版本
"lodash": "4.17.21" # 而非 "^4.17.21"
# 使用 lock 文件确保一致性
pnpm-lock.yaml
package-lock.json
yarn.lock
安全实践:
- 定期更新依赖,及时修复漏洞
- 使用私有 registry(如 Verdaccio)管控内部包
- 审查新引入依赖的维护状态和安全记录
十四、性能预算与构建优化
1 性能预算定义
javascript
// budgets.config.js
module.exports = {
budgets: [
{
type: 'bundle',
name: 'main',
maximumWarning: '150kb',
maximumError: '200kb'
},
{
type: 'bundle',
name: 'vendor',
maximumWarning: '250kb',
maximumError: '300kb'
},
{
type: 'asset',
name: '*.jpg',
maximumWarning: '100kb',
maximumError: '200kb'
}
]
};
2 CI 中的性能检查
yaml
- name: Build with budget check
run: pnpm build
- name: Check bundle size
run: |
BUNDLE_SIZE=$(du -k dist/assets/main.*.js | cut -f1)
if [ $BUNDLE_SIZE -gt 200 ]; then
echo "错误: main bundle 超过 200KB (当前: ${BUNDLE_SIZE}KB)"
exit 1
fi
3 构建产物优化清单
| 优化项 | 工具/方法 | 预期效果 |
|---|---|---|
| 代码分割 | Vite/Rollup manualChunks |
减少首屏加载 |
| Tree Shaking | ESM + 副作用标记 | 移除未使用代码 |
| Gzip/Brotli | 构建时生成压缩文件 | 减少传输体积 |
| 图片优化 | vite-plugin-imagemin |
图片体积减少 50%+ |
| 字体子集化 | fontmin / subset-font |
只打包使用的字符 |
| 懒加载路由 | React.lazy() / defineAsyncComponent |
按需加载页面 |
十五、多环境配置管理
1 环境配置分层
config/
├── default.json # 默认配置(所有环境共享)
├── development.json # 开发环境覆盖
├── staging.json # 测试环境覆盖
└── production.json # 生产环境覆盖
javascript
// 配置加载逻辑
import defaultConfig from './config/default.json';
import devConfig from './config/development.json';
import prodConfig from './config/production.json';
const env = import.meta.env.VITE_APP_ENV || 'development';
const envConfig = {
development: devConfig,
staging: stagingConfig,
production: prodConfig
}[env];
export const config = { ...defaultConfig, ...envConfig };
2 环境变量命名规范
bash
# 公开变量(构建时注入,客户端可见)
VITE_API_URL=https://api.example.com
VITE_APP_NAME=MyApp
# 私有变量(仅服务端/CI 使用)
API_SECRET_KEY=xxx
DEPLOY_TOKEN=xxx
Vite 环境变量规则:
- 只有以
VITE_开头的变量才会暴露给客户端代码 - 其他变量仅在 Node.js 环境中可用
3 动态配置(无需重新构建)
javascript
// 运行时获取配置,避免每次改配置都要重新构建
async function loadRuntimeConfig() {
const response = await fetch('/config.json');
return response.json();
}
// 应用启动时加载
const runtimeConfig = await loadRuntimeConfig();
// 后续使用 runtimeConfig.apiUrl 等
十六、团队协作与 Code Review 自动化
1 PR 模板与检查清单
markdown
<!-- .github/pull_request_template.md -->
## 变更描述
## 检查清单
- [ ] 代码通过 ESLint 检查
- [ ] 单元测试全部通过
- [ ] 手动测试核心流程
- [ ] 更新相关文档
- [ ] 添加/更新 CHANGELOG
## 截图(如适用)
## 关联 Issue
Closes #
2 自动化 PR 检查
yaml
# .github/workflows/pr.yml
name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
pr-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # 需要完整历史检查提交信息
- name: Check commit messages
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
- name: Check PR title
run: |
echo "${{ github.event.pull_request.title }}" | npx commitlint
- name: Label PR by files changed
uses: actions/labeler@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
3 代码覆盖率门禁
yaml
- name: Check coverage
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
verbose: true
覆盖率策略:
- 新代码覆盖率不低于 80%
- 整体覆盖率不低于 60%
- 核心模块覆盖率不低于 90%
十七、故障排查与 CI 调试技巧
1 本地模拟 CI 环境
bash
# 使用 act 工具本地运行 GitHub Actions
# 安装
brew install act
# 运行完整工作流
act
# 运行特定 job
act -j build
# 使用特定事件触发
act pull_request
2 CI 故障排查清单
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 依赖安装失败 | lock 文件与 package.json 不一致 | 删除 lock 文件重新生成 |
| 构建内存溢出 | Node.js 内存限制 | NODE_OPTIONS=--max-old-space-size=4096 |
| 测试随机失败 | 时序问题、未清理状态 | 添加 beforeEach 清理,使用 waitFor |
| 权限 denied | Secrets 未配置或名称错误 | 检查 Repository Settings → Secrets |
| 缓存未命中 | 缓存 key 配置错误 | 检查 actions/cache 的 key 和 path |
| 部署后 404 | 路由配置问题 | 检查 Nginx/服务器 rewrite 规则 |
3 调试技巧
yaml
# 在 CI 中启用调试输出
- name: Debug info
run: |
echo "Node version: $(node -v)"
echo "NPM version: $(npm -v)"
echo "Working directory: $(pwd)"
echo "Directory contents:"
ls -la
echo "Environment variables:"
env | grep VITE_
bash
# SSH 连接到运行中的 CI 实例(GitHub Actions)
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ failure() }}
十八、未来趋势与展望
1 前端部署的演进方向
| 阶段 | 特点 | 代表 |
|---|---|---|
| 手动 FTP 上传 | 无版本控制、易出错 | 早期个人站点 |
| 脚本化部署 | 脚本代替手动操作 | Shell / Python 脚本 |
| CI/CD 流水线 | 自动化、可追踪 | GitHub Actions / Jenkins |
| 平台化部署 | 零配置、边缘部署 | Vercel / Netlify / Cloudflare Pages |
| 基础设施即代码 | 声明式、可复现 | Terraform / Pulumi |
2 边缘计算与前端
javascript
// Cloudflare Workers 边缘函数
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// A/B 测试在边缘层实现
const variant = Math.random() < 0.5 ? 'a' : 'b';
url.pathname = `/variant-${variant}${url.pathname}`;
return fetch(url);
}
};
边缘部署优势:
- 全球低延迟(就近响应)
- 减少源站压力
- 动态内容在边缘处理