学习目标
- 掌握生产环境构建和优化
- 学会Docker容器化部署
- 理解CI/CD流程实现
- 掌握监控和日志系统
- 理解安全最佳实践
- 构建完整的部署方案
学习时间安排
总时长:8-9小时
- 生产环境构建:1.5小时
- Docker容器化:2小时
- CI/CD流程:2小时
- 监控和日志:1.5小时
- 安全实践:1小时
- 完整部署实践:2-3小时
第一部分:生产环境构建 (1.5小时)
1.1 生产环境优化配置
Webpack生产配置(详细注释版)
javascript
// webpack.prod.js
// 导入webpack
const webpack = require('webpack');
// 导入路径处理
const path = require('path');
// 导入插件
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
// 导出生产环境配置
module.exports = {
// 生产模式
mode: 'production',
// 入口文件
entry: {
main: './src/index.js',
// 可以添加多个入口点进行代码分割
},
// 输出配置
output: {
// 输出目录
path: path.resolve(__dirname, 'build'),
// 输出文件名,使用内容哈希实现缓存
filename: 'static/js/[name].[contenthash:8].js',
// 代码分割后的文件名
chunkFilename: 'static/js/[name].[contenthash:8].chunk.js',
// 资源文件名
assetModuleFilename: 'static/media/[name].[hash:8][ext]',
// 清理输出目录
clean: true,
// 公共路径
publicPath: '/',
},
// 模块解析配置
resolve: {
// 文件扩展名
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
// 别名配置
alias: {
'@': path.resolve(__dirname, 'src'),
'components': path.resolve(__dirname, 'src/components'),
'utils': path.resolve(__dirname, 'src/utils'),
'store': path.resolve(__dirname, 'src/store'),
},
},
// 模块处理规则
module: {
rules: [
// JavaScript/TypeScript文件处理
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
// 缓存配置,提升构建速度
cacheDirectory: true,
cacheCompression: false,
// Babel配置
presets: [
['@babel/preset-env', {
useBuiltIns: 'entry',
corejs: 3,
}],
['@babel/preset-react', {
runtime: 'automatic',
}],
'@babel/preset-typescript',
],
plugins: [
// 生产环境移除console和debugger
['transform-remove-console', { exclude: ['error', 'warn'] }],
],
},
},
},
// CSS文件处理
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
// CSS模块化配置
localIdentName: '[name]__[local]--[hash:base64:5]',
},
},
},
'postcss-loader',
],
},
// 图片资源处理
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
// 小于8KB的图片转为base64
maxSize: 8 * 1024,
},
},
},
// 字体文件处理
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
// 优化配置
optimization: {
// 最小化配置
minimize: true,
// 最小化工具
minimizer: [
// JavaScript压缩
new TerserPlugin({
terserOptions: {
compress: {
// 移除console
drop_console: true,
// 移除debugger
drop_debugger: true,
// 移除未使用的代码
pure_funcs: ['console.log'],
},
format: {
// 移除注释
comments: false,
},
},
extractComments: false,
}),
// CSS压缩
new CssMinimizerPlugin(),
],
// 代码分割配置
splitChunks: {
chunks: 'all',
cacheGroups: {
// 提取vendor代码
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
reuseExistingChunk: true,
},
// 提取公共代码
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
},
// 提取React相关代码
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20,
reuseExistingChunk: true,
},
},
},
// 运行时chunk
runtimeChunk: {
name: 'runtime',
},
},
// 插件配置
plugins: [
// 定义环境变量
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.API_URL': JSON.stringify(process.env.API_URL || 'https://api.example.com'),
}),
// Gzip压缩
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8,
}),
// Bundle分析(可选)
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean),
// 性能提示
performance: {
// 关闭性能提示
hints: false,
// 或者设置最大资源大小
// maxEntrypointSize: 512000,
// maxAssetSize: 512000,
},
// Source Map配置(生产环境使用source-map)
devtool: 'source-map',
};
Vite生产配置(详细注释版)
javascript
// vite.config.js
// 导入vite
import { defineConfig } from 'vite';
// 导入React插件
import react from '@vitejs/plugin-react';
// 导入路径处理
import path from 'path';
// 导出Vite配置
export default defineConfig({
// 插件配置
plugins: [
react({
// React快速刷新
fastRefresh: true,
// Babel配置
babel: {
plugins: [
// 生产环境移除console
process.env.NODE_ENV === 'production' && [
'transform-remove-console',
{ exclude: ['error', 'warn'] }
],
].filter(Boolean),
},
}),
],
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'components': path.resolve(__dirname, 'src/components'),
'utils': path.resolve(__dirname, 'src/utils'),
'store': path.resolve(__dirname, 'src/store'),
},
},
// 构建配置
build: {
// 输出目录
outDir: 'dist',
// 资源目录
assetsDir: 'static',
// 源代码映射
sourcemap: true,
// 最小化
minify: 'terser',
// Terser选项
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
// 代码分割
rollupOptions: {
output: {
// 手动代码分割
manualChunks: {
// React相关
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
// Redux相关
'redux-vendor': ['@reduxjs/toolkit', 'react-redux'],
// 工具库
'utils-vendor': ['lodash', 'axios', 'dayjs'],
},
// 文件命名
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
},
},
// 块大小警告限制
chunkSizeWarningLimit: 1000,
},
// 服务器配置(开发环境)
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// 环境变量配置
envPrefix: 'VITE_',
});
1.2 环境变量配置
环境变量文件(详细注释版)
bash
# .env.development
# 开发环境变量
REACT_APP_API_URL=http://localhost:8080/api
REACT_APP_ENV=development
REACT_APP_DEBUG=true
REACT_APP_VERSION=1.0.0
# .env.production
# 生产环境变量
REACT_APP_API_URL=https://api.example.com
REACT_APP_ENV=production
REACT_APP_DEBUG=false
REACT_APP_VERSION=1.0.0
# .env.staging
# 预发布环境变量
REACT_APP_API_URL=https://staging-api.example.com
REACT_APP_ENV=staging
REACT_APP_DEBUG=true
REACT_APP_VERSION=1.0.0
环境变量使用(详细注释版)
typescript
// src/config/env.ts
// 环境变量配置类型
interface EnvConfig {
apiUrl: string;
env: string;
debug: boolean;
version: string;
}
// 获取环境变量配置
function getEnvConfig(): EnvConfig {
return {
// API地址
apiUrl: process.env.REACT_APP_API_URL || 'http://localhost:8080/api',
// 环境类型
env: process.env.REACT_APP_ENV || 'development',
// 是否开启调试
debug: process.env.REACT_APP_DEBUG === 'true',
// 版本号
version: process.env.REACT_APP_VERSION || '1.0.0',
};
}
// 导出环境配置
export const envConfig = getEnvConfig();
// 导出环境判断函数
export const isDevelopment = () => envConfig.env === 'development';
export const isProduction = () => envConfig.env === 'production';
export const isStaging = () => envConfig.env === 'staging';
第二部分:Docker容器化 (2小时)
2.1 Dockerfile配置
多阶段构建Dockerfile(详细注释版)
dockerfile
# Dockerfile
# 第一阶段:构建阶段
FROM node:18-alpine AS builder
# 设置工作目录
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖(使用npm ci提升安装速度和可靠性)
RUN npm ci --only=production=false
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# 第二阶段:生产阶段
FROM nginx:alpine AS production
# 安装curl用于健康检查
RUN apk add --no-cache curl
# 复制构建产物到nginx目录
COPY --from=builder /app/build /usr/share/nginx/html
# 复制nginx配置文件
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/ || exit 1
# 启动nginx
CMD ["nginx", "-g", "daemon off;"]
Nginx配置文件(详细注释版)
nginx
# nginx.conf
# 上游服务器配置(如果需要代理API)
upstream api_server {
server backend:8080;
}
# HTTP服务器配置
server {
# 监听端口
listen 80;
# 服务器名称
server_name localhost;
# 根目录
root /usr/share/nginx/html;
# 索引文件
index index.html;
# 字符集
charset utf-8;
# 日志配置
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip压缩配置
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
# 静态资源缓存配置
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# API代理配置
location /api {
proxy_pass http://api_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# React路由配置(所有路由都返回index.html)
location / {
try_files $uri $uri/ /index.html;
}
# 安全头配置
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
2.2 Docker Compose配置
Docker Compose文件(详细注释版)
yaml
# docker-compose.yml
version: '3.8'
# 服务定义
services:
# 前端服务
frontend:
# 构建配置
build:
# 构建上下文
context: .
# Dockerfile路径
dockerfile: Dockerfile
# 构建参数
args:
- NODE_ENV=production
# 容器名称
container_name: react-app
# 端口映射
ports:
- "80:80"
# 环境变量
environment:
- REACT_APP_API_URL=http://localhost:8080/api
# 依赖服务
depends_on:
- backend
# 重启策略
restart: unless-stopped
# 网络配置
networks:
- app-network
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 后端服务(示例)
backend:
image: node:18-alpine
container_name: backend-api
working_dir: /app
volumes:
- ./backend:/app
ports:
- "8080:8080"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
networks:
- app-network
restart: unless-stopped
# 数据库服务(示例)
db:
image: postgres:15-alpine
container_name: postgres-db
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
restart: unless-stopped
# 网络配置
networks:
app-network:
driver: bridge
# 数据卷配置
volumes:
postgres-data:
2.3 Docker构建和部署脚本
构建脚本(详细注释版)
bash
#!/bin/bash
# build.sh
# Docker构建和部署脚本
# 设置错误时退出
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 打印信息函数
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查Docker是否安装
check_docker() {
if ! command -v docker &> /dev/null; then
print_error "Docker is not installed. Please install Docker first."
exit 1
fi
print_info "Docker is installed"
}
# 检查Docker Compose是否安装
check_docker_compose() {
if ! command -v docker-compose &> /dev/null; then
print_error "Docker Compose is not installed. Please install Docker Compose first."
exit 1
fi
print_info "Docker Compose is installed"
}
# 构建Docker镜像
build_image() {
print_info "Building Docker image..."
docker build -t react-app:latest .
print_info "Docker image built successfully"
}
# 运行容器
run_container() {
print_info "Starting containers..."
docker-compose up -d
print_info "Containers started successfully"
}
# 停止容器
stop_container() {
print_info "Stopping containers..."
docker-compose down
print_info "Containers stopped successfully"
}
# 查看日志
view_logs() {
print_info "Viewing logs..."
docker-compose logs -f
}
# 清理未使用的资源
cleanup() {
print_info "Cleaning up unused resources..."
docker system prune -f
print_info "Cleanup completed"
}
# 主函数
main() {
case "$1" in
build)
check_docker
build_image
;;
start)
check_docker
check_docker_compose
run_container
;;
stop)
check_docker_compose
stop_container
;;
logs)
check_docker_compose
view_logs
;;
cleanup)
check_docker
cleanup
;;
*)
echo "Usage: $0 {build|start|stop|logs|cleanup}"
exit 1
;;
esac
}
# 执行主函数
main "$@"
第三部分:CI/CD流程 (2小时)
3.1 GitHub Actions配置
CI/CD工作流(详细注释版)
yaml
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
# 触发条件
on:
# 推送到主分支时触发
push:
branches:
- main
- develop
# 创建Pull Request时触发
pull_request:
branches:
- main
- develop
# 手动触发
workflow_dispatch:
# 环境变量
env:
NODE_VERSION: '18'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# 工作流任务
jobs:
# 代码检查和测试
lint-and-test:
name: Lint and Test
runs-on: ubuntu-latest
steps:
# 检出代码
- name: Checkout code
uses: actions/checkout@v3
# 设置Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
# 安装依赖
- name: Install dependencies
run: npm ci
# 运行ESLint
- name: Run ESLint
run: npm run lint
# 运行类型检查
- name: Run TypeScript check
run: npm run type-check
# 运行测试
- name: Run tests
run: npm run test:coverage
# 上传测试覆盖率
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
# 构建应用
build:
name: Build Application
runs-on: ubuntu-latest
needs: lint-and-test
steps:
# 检出代码
- name: Checkout code
uses: actions/checkout@v3
# 设置Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
# 安装依赖
- name: Install dependencies
run: npm ci
# 构建应用
- name: Build application
run: npm run build
env:
REACT_APP_API_URL: ${{ secrets.API_URL }}
REACT_APP_ENV: production
# 上传构建产物
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: build/
retention-days: 7
# 构建Docker镜像
build-docker:
name: Build Docker Image
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
# 检出代码
- name: Checkout code
uses: actions/checkout@v3
# 登录到容器注册表
- name: Login to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# 设置Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# 构建并推送Docker镜像
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
# 部署到生产环境
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build-docker
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
# 检出代码
- name: Checkout code
uses: actions/checkout@v3
# 部署到服务器
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/app
docker-compose pull
docker-compose up -d
docker system prune -f
# 健康检查
- name: Health check
run: |
sleep 10
curl -f https://example.com/health || exit 1
3.2 GitLab CI配置
GitLab CI配置文件(详细注释版)
yaml
# .gitlab-ci.yml
# GitLab CI/CD配置文件
# 定义阶段
stages:
- lint
- test
- build
- deploy
# 定义变量
variables:
NODE_VERSION: "18"
DOCKER_IMAGE: $CI_REGISTRY_IMAGE
DOCKER_TAG: $CI_COMMIT_REF_SLUG
# 缓存配置
cache:
paths:
- node_modules/
- .npm/
# Lint阶段
lint:
stage: lint
image: node:$NODE_VERSION
script:
- npm ci
- npm run lint
- npm run type-check
only:
- merge_requests
- main
- develop
# 测试阶段
test:
stage: test
image: node:$NODE_VERSION
script:
- npm ci
- npm run test:coverage
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
paths:
- coverage/
expire_in: 1 week
only:
- merge_requests
- main
- develop
# 构建阶段
build:
stage: build
image: node:$NODE_VERSION
script:
- npm ci
- npm run build
artifacts:
paths:
- build/
expire_in: 1 week
only:
- main
- develop
# Docker构建阶段
docker-build:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $DOCKER_IMAGE:$DOCKER_TAG .
- docker push $DOCKER_IMAGE:$DOCKER_TAG
only:
- main
# 部署到生产环境
deploy-production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan $DEPLOY_HOST >> ~/.ssh/known_hosts
script:
- ssh $DEPLOY_USER@$DEPLOY_HOST "cd /opt/app && docker-compose pull && docker-compose up -d"
environment:
name: production
url: https://example.com
only:
- main
when: manual
第四部分:监控和日志 (1.5小时)
4.1 应用监控
性能监控Hook(详细注释版)
typescript
// src/hooks/usePerformanceMonitor.ts
// 导入React
import { useEffect, useRef } from 'react';
// 性能指标接口
interface PerformanceMetrics {
// 首次内容绘制时间
fcp: number | null;
// 最大内容绘制时间
lcp: number | null;
// 首次输入延迟
fid: number | null;
// 累积布局偏移
cls: number | null;
// 总阻塞时间
tbt: number | null;
}
// 性能监控Hook
export function usePerformanceMonitor() {
// 使用useRef保存指标
const metricsRef = useRef<PerformanceMetrics>({
fcp: null,
lcp: null,
fid: null,
cls: null,
tbt: null,
});
useEffect(() => {
// 检查Performance Observer API支持
if (typeof window === 'undefined' || !('PerformanceObserver' in window)) {
return;
}
// 观察性能指标
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 处理不同类型的性能指标
if (entry.entryType === 'paint') {
const paintEntry = entry as PerformancePaintTiming;
if (paintEntry.name === 'first-contentful-paint') {
metricsRef.current.fcp = paintEntry.startTime;
// 发送到监控服务
sendMetric('fcp', paintEntry.startTime);
}
}
if (entry.entryType === 'largest-contentful-paint') {
const lcpEntry = entry as PerformanceEntry & {
renderTime?: number;
loadTime?: number;
};
metricsRef.current.lcp = lcpEntry.renderTime || lcpEntry.loadTime || null;
if (metricsRef.current.lcp) {
sendMetric('lcp', metricsRef.current.lcp);
}
}
if (entry.entryType === 'first-input') {
const fidEntry = entry as PerformanceEventTiming;
metricsRef.current.fid = fidEntry.processingStart - fidEntry.startTime;
if (metricsRef.current.fid) {
sendMetric('fid', metricsRef.current.fid);
}
}
if (entry.entryType === 'layout-shift') {
const clsEntry = entry as PerformanceEntry & { value?: number };
if (!clsEntry.hadRecentInput && clsEntry.value) {
metricsRef.current.cls = (metricsRef.current.cls || 0) + clsEntry.value;
sendMetric('cls', metricsRef.current.cls);
}
}
}
});
// 观察不同类型的性能指标
try {
observer.observe({ entryTypes: ['paint', 'largest-contentful-paint', 'first-input', 'layout-shift'] });
} catch (e) {
console.warn('Performance Observer not fully supported:', e);
}
// 清理函数
return () => {
observer.disconnect();
};
}, []);
// 发送指标到监控服务
const sendMetric = (name: string, value: number) => {
// 在实际应用中,这里会发送到监控服务(如Google Analytics、Sentry等)
if (process.env.NODE_ENV === 'production') {
// 发送到监控服务
// analytics.track('performance_metric', { name, value });
console.log(`Performance metric: ${name} = ${value}ms`);
}
};
return metricsRef.current;
}
4.2 错误日志系统
日志服务(详细注释版)
typescript
// src/services/loggingService.ts
// 日志级别枚举
export enum LogLevel {
DEBUG = 'DEBUG',
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR',
}
// 日志接口
interface LogEntry {
// 日志级别
level: LogLevel;
// 日志消息
message: string;
// 时间戳
timestamp: string;
// 额外数据
data?: any;
// 用户信息
user?: {
id: string;
email?: string;
};
// 环境信息
environment?: string;
// 错误堆栈
stack?: string;
}
// 日志服务类
class LoggingService {
private logs: LogEntry[] = [];
private maxLogs = 100;
private flushInterval = 60000; // 1分钟
constructor() {
// 定期刷新日志
setInterval(() => {
this.flush();
}, this.flushInterval);
// 页面卸载时刷新日志
window.addEventListener('beforeunload', () => {
this.flush();
});
}
// 记录日志
private log(level: LogLevel, message: string, data?: any) {
const entry: LogEntry = {
level,
message,
timestamp: new Date().toISOString(),
data,
environment: process.env.NODE_ENV,
};
// 添加到日志数组
this.logs.push(entry);
// 限制日志数量
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
// 控制台输出
this.consoleLog(level, message, data);
// 如果是错误级别,立即发送
if (level === LogLevel.ERROR) {
this.sendLog(entry);
}
}
// 控制台输出
private consoleLog(level: LogLevel, message: string, data?: any) {
const styles = {
[LogLevel.DEBUG]: 'color: gray',
[LogLevel.INFO]: 'color: blue',
[LogLevel.WARN]: 'color: orange',
[LogLevel.ERROR]: 'color: red',
};
console.log(`%c[${level}] ${message}`, styles[level], data || '');
}
// 发送日志到服务器
private async sendLog(entry: LogEntry) {
try {
// 在实际应用中,这里会发送到日志服务器
// await fetch('/api/logs', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(entry),
// });
console.log('Log sent:', entry);
} catch (error) {
console.error('Failed to send log:', error);
}
}
// 刷新所有日志
async flush() {
if (this.logs.length === 0) {
return;
}
const logsToSend = [...this.logs];
this.logs = [];
try {
// 批量发送日志
// await fetch('/api/logs/batch', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(logsToSend),
// });
console.log('Logs flushed:', logsToSend.length);
} catch (error) {
console.error('Failed to flush logs:', error);
// 如果发送失败,重新添加到日志数组
this.logs.unshift(...logsToSend);
}
}
// 公共方法
debug(message: string, data?: any) {
this.log(LogLevel.DEBUG, message, data);
}
info(message: string, data?: any) {
this.log(LogLevel.INFO, message, data);
}
warn(message: string, data?: any) {
this.log(LogLevel.WARN, message, data);
}
error(message: string, error?: Error | any) {
const entry: LogEntry = {
level: LogLevel.ERROR,
message: error?.message || message,
timestamp: new Date().toISOString(),
data: error,
stack: error?.stack,
environment: process.env.NODE_ENV,
};
this.logs.push(entry);
this.consoleLog(LogLevel.ERROR, message, error);
this.sendLog(entry);
}
}
// 创建全局日志服务实例
const loggingService = new LoggingService();
// 导出日志服务
export default loggingService;
第五部分:安全实践 (1小时)
5.1 安全配置
安全中间件(详细注释版)
typescript
// src/utils/security.ts
// 安全工具函数
// XSS防护:转义HTML
export function escapeHtml(text: string): string {
const map: Record<string, string> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return text.replace(/[&<>"']/g, (m) => map[m]);
}
// CSRF Token生成
export function generateCSRFToken(): string {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
}
// 验证CSRF Token
export function validateCSRFToken(token: string, storedToken: string): boolean {
return token === storedToken;
}
// 内容安全策略配置
export const cspConfig = {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'", 'data:'],
'connect-src': ["'self'", 'https://api.example.com'],
'frame-ancestors': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
};
// 生成CSP头
export function generateCSPHeader(): string {
return Object.entries(cspConfig)
.map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
.join('; ');
}
安全Headers配置(详细注释版)
typescript
// src/config/securityHeaders.ts
// 安全头配置
export const securityHeaders = {
// 防止点击劫持
'X-Frame-Options': 'DENY',
// 防止MIME类型嗅探
'X-Content-Type-Options': 'nosniff',
// XSS保护
'X-XSS-Protection': '1; mode=block',
// 引用策略
'Referrer-Policy': 'strict-origin-when-cross-origin',
// 权限策略
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
// 严格传输安全(HTTPS)
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
};
第六部分:完整部署实践 (2-3小时)
部署检查清单
部署前检查(详细注释版)
typescript
// scripts/pre-deploy-check.ts
// 部署前检查脚本
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
// 检查项接口
interface CheckItem {
name: string;
check: () => boolean;
message: string;
}
// 检查函数数组
const checks: CheckItem[] = [
{
name: '环境变量检查',
check: () => {
const requiredEnvVars = [
'REACT_APP_API_URL',
'REACT_APP_ENV',
];
return requiredEnvVars.every(envVar => process.env[envVar]);
},
message: '所有必需的环境变量都已设置',
},
{
name: '构建文件检查',
check: () => {
const buildDir = path.join(process.cwd(), 'build');
return fs.existsSync(buildDir) && fs.statSync(buildDir).isDirectory();
},
message: '构建目录存在',
},
{
name: 'index.html检查',
check: () => {
const indexPath = path.join(process.cwd(), 'build', 'index.html');
return fs.existsSync(indexPath);
},
message: 'index.html文件存在',
},
{
name: '静态资源检查',
check: () => {
const staticDir = path.join(process.cwd(), 'build', 'static');
return fs.existsSync(staticDir);
},
message: '静态资源目录存在',
},
{
name: 'Dockerfile检查',
check: () => {
const dockerfilePath = path.join(process.cwd(), 'Dockerfile');
return fs.existsSync(dockerfilePath);
},
message: 'Dockerfile存在',
},
{
name: 'Nginx配置检查',
check: () => {
const nginxConfigPath = path.join(process.cwd(), 'nginx.conf');
return fs.existsSync(nginxConfigPath);
},
message: 'Nginx配置文件存在',
},
];
// 运行检查
function runChecks() {
console.log('开始部署前检查...\n');
let allPassed = true;
checks.forEach((check, index) => {
const passed = check.check();
const status = passed ? '✓' : '✗';
const statusColor = passed ? '\x1b[32m' : '\x1b[31m';
console.log(`${statusColor}${status}\x1b[0m [${index + 1}/${checks.length}] ${check.name}`);
console.log(` ${check.message}\n`);
if (!passed) {
allPassed = false;
}
});
if (allPassed) {
console.log('\x1b[32m所有检查通过!可以开始部署。\x1b[0m\n');
process.exit(0);
} else {
console.log('\x1b[31m部分检查未通过,请修复后重试。\x1b[0m\n');
process.exit(1);
}
}
// 执行检查
runChecks();
部署脚本(详细注释版)
bash
#!/bin/bash
# deploy.sh
# 完整部署脚本
set -e
# 配置
ENVIRONMENT=${1:-production}
VERSION=${2:-latest}
DEPLOY_PATH="/opt/app"
echo "开始部署到 $ENVIRONMENT 环境..."
# 1. 运行部署前检查
echo "运行部署前检查..."
npm run pre-deploy-check
# 2. 构建应用
echo "构建应用..."
npm run build
# 3. 构建Docker镜像
echo "构建Docker镜像..."
docker build -t react-app:$VERSION .
# 4. 推送到镜像仓库
echo "推送镜像到仓库..."
docker tag react-app:$VERSION registry.example.com/react-app:$VERSION
docker push registry.example.com/react-app:$VERSION
# 5. 部署到服务器
echo "部署到服务器..."
ssh user@server << EOF
cd $DEPLOY_PATH
docker pull registry.example.com/react-app:$VERSION
docker-compose down
docker-compose up -d
docker system prune -f
EOF
# 6. 健康检查
echo "执行健康检查..."
sleep 10
curl -f https://example.com/health || exit 1
echo "部署完成!"
练习题目
基础练习
- 构建配置练习
bash
# 练习1:配置生产环境构建
# 实现:Webpack/Vite生产配置、代码分割、资源优化
# 包含:压缩、Tree Shaking、代码分割
# 练习2:配置环境变量
# 实现:多环境配置、环境变量管理
# 包含:开发、测试、生产环境配置
- Docker练习
bash
# 练习3:创建Dockerfile
# 实现:多阶段构建、优化镜像大小
# 包含:构建阶段、生产阶段、健康检查
# 练习4:配置Docker Compose
# 实现:多服务编排、网络配置
# 包含:前端、后端、数据库服务
进阶练习
- CI/CD练习
bash
# 练习5:配置GitHub Actions
# 实现:自动化测试、构建、部署
# 包含:代码检查、测试、Docker构建、部署
# 练习6:实现完整部署流程
# 实现:自动化部署、回滚机制
# 包含:部署脚本、健康检查、监控