name: web-build-deploy description: 构建和部署React网络应用。用于配置构建、部署到Vercel/Netlify、设置CI/CD、Docker或管理环境。
Web Build & Deploy (React)
Vercel 部署
快速部署
# 安装 Vercel CLI
npm i -g vercel
# 部署
vercel
# 部署到生产环境
vercel --prod
配置 (vercel.json)
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{ "source": "/(.*)", "destination": "/" }
],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
]
}
环境变量
# 通过 CLI 添加
vercel env add NEXT_PUBLIC_API_URL
# 或在 Vercel 仪表板:设置 > 环境变量
# 在代码中访问
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
预览部署
每次推送到分支会自动创建预览URL:
https://project-git-branch-username.vercel.app
Netlify 部署
快速部署
# 安装 Netlify CLI
npm i -g netlify-cli
# 登录
netlify login
# 部署预览
netlify deploy
# 部署到生产环境
netlify deploy --prod
配置 (netlify.toml)
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
[build.environment]
NODE_VERSION = "18"
环境变量
# 通过 CLI 添加
netlify env:set API_URL https://api.example.com
# 或在 Netlify 仪表板:站点设置 > 环境变量
Docker 部署
Dockerfile (多阶段构建)
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
# 复制构建文件
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制 nginx 配置以支持 SPA 路由
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf (SPA 路由)
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 text/xml application/xml;
# 缓存静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA 回退
location / {
try_files $uri $uri/ /index.html;
}
# 安全头信息
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
}
Docker Compose
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "80:80"
environment:
- NODE_ENV=production
restart: unless-stopped
# 带后端
api:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/app
depends_on:
- db
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=app
volumes:
postgres_data:
构建 & 运行
# 构建镜像
docker build -t myapp:latest .
# 运行容器
docker run -p 80:80 myapp:latest
# 使用 docker-compose
docker-compose up -d
docker-compose down
GitHub Actions CI/CD
基本工作流
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
env:
VITE_API_URL: ${{ secrets.API_URL }}
- name: Deploy to Vercel
if: github.ref == 'refs/heads/main'
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'
带预览部署
name: Preview
on: [pull_request]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy Preview
uses: amondnet/vercel-action@v25
id: deploy
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
- name: Comment PR
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 Preview deployed to: ${{ steps.deploy.outputs.preview-url }}'
})
环境配置
Vite
// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
define: {
'process.env.API_URL': JSON.stringify(env.VITE_API_URL),
},
};
});
# .env.development
VITE_API_URL=http://localhost:8000
# .env.production
VITE_API_URL=https://api.example.com
Next.js
# .env.local (不提交)
DATABASE_URL=postgresql://localhost:5432/dev
# .env.production
NEXT_PUBLIC_API_URL=https://api.example.com
// 在代码中访问
// 客户端(必须以 NEXT_PUBLIC_ 开头)
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// 仅服务器端
const dbUrl = process.env.DATABASE_URL;
构建优化
Vite 构建分析
# 安装分析器
npm i -D rollup-plugin-visualizer
# 添加到 vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
filename: 'stats.html',
open: true,
}),
],
});
# 构建和分析
npm run build
代码分割
// 基于路由的分割
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
// 组件分割
const HeavyChart = lazy(() => import('./components/HeavyChart'));
缓存策略
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
},
},
},
},
});
健康检查 & 监控
健康检查端点
// 适用于 Docker/Kubernetes
// api/health.ts (Next.js)
export async function GET() {
return Response.json({ status: 'healthy', timestamp: Date.now() });
}
错误跟踪 (Sentry)
// sentry.client.config.ts
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.1,
});
// 包装应用
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
常见问题
| 问题 | 解决方案 |
|---|---|
| 刷新时出现404(SPA) | 为 SPA 回退配置服务器 |
| 环境变量未定义 | 检查前缀(VITE_, NEXT_PUBLIC_) |
| CI上构建失败 | 检查 Node 版本,清除缓存 |
| Docker 镜像过大 | 使用多阶段构建,alpine 基础 |
| 构建缓慢 | 启用缓存,平行化 |
部署清单
部署到生产环境前:
- [ ] 设置环境变量
- [ ] 本地构建成功
- [ ] 测试通过
- [ ] 配置安全头信息
- [ ] 启用错误跟踪
- [ ] 性能优化(捆绑大小,代码分割)
- [ ] SEO 元标签(如适用)
- [ ] 启用 SSL/HTTPS
- [ ] 配置自定义域名
- [ ] 健康检查端点工作正常