在将 Tailwind CSS 项目部署到生产环境时,需要做好各项配置和优化工作。本节将详细介绍如何配置和优化部署环境。
环境配置
Docker 配置
# Dockerfile
# 构建阶段
FROM node:16-alpine as builderWORKDIR /appCOPY package*.json ./
RUN npm ciCOPY . .
RUN npm run build# 生产阶段
FROM nginx:alpine# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.confEXPOSE 80CMD ["nginx", "-g", "daemon off;"]
Nginx 配置
# nginx.conf
server {listen 80;server_name localhost;root /usr/share/nginx/html;index index.html;# Gzip 压缩gzip on;gzip_vary on;gzip_proxied any;gzip_comp_level 6;gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;# 缓存配置location ~* \.(?:css|js)$ {expires 1y;access_log off;add_header Cache-Control "public";}location / {try_files $uri $uri/ /index.html;}
}
构建配置
生产环境配置
// tailwind.config.js
module.exports = {mode: 'jit',purge: {enabled: process.env.NODE_ENV === 'production',content: ['./src/**/*.{js,jsx,ts,tsx}','./public/index.html'],options: {safelist: [/^bg-/,/^text-/,'prose','prose-lg']}},theme: {extend: {// 生产环境特定配置}}
}
构建脚本
// scripts/build.js
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');module.exports = {mode: 'production',optimization: {minimizer: [new TerserPlugin({terserOptions: {compress: {drop_console: true,},},}),new CssMinimizerPlugin({minimizerOptions: {preset: ['default',{discardComments: { removeAll: true },},],},}),],},plugins: [new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify('production'),}),],
};
CI/CD 配置
GitHub Actions
# .github/workflows/deploy.yml
name: Deployon:push:branches: [ main ]jobs:deploy:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Setup Node.jsuses: actions/setup-node@v2with:node-version: '16'cache: 'npm'- name: Install dependenciesrun: npm ci- name: Buildrun: npm run buildenv:NODE_ENV: production- name: Deploy to serveruses: appleboy/scp-action@masterwith:host: ${{ secrets.HOST }}username: ${{ secrets.USERNAME }}key: ${{ secrets.SSH_KEY }}source: "dist/"target: "/var/www/html"
部署脚本
#!/bin/bash
# deploy.sh# 环境变量
ENV=$1
CONFIG_FILE="config.$ENV.json"# 验证参数
if [ -z "$ENV" ]; thenecho "Please specify environment (dev/staging/prod)"exit 1
fi# 构建项目
echo "Building for $ENV environment..."
npm run build:$ENV# 部署到服务器
echo "Deploying to $ENV server..."
rsync -avz --delete dist/ user@server:/path/to/$ENV/# 清理缓存
echo "Clearing cache..."
curl -X POST https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache \-H "Authorization: Bearer $CF_TOKEN" \-H "Content-Type: application/json" \--data '{"purge_everything":true}'echo "Deployment completed!"
性能优化
静态资源优化
// webpack.prod.js
const CompressionPlugin = require('compression-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');module.exports = {plugins: [new CompressionPlugin({test: /\.(js|css|html|svg)$/,algorithm: 'gzip',}),new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminMinify,options: {plugins: [['mozjpeg', { quality: 80 }],['pngquant', { quality: [0.6, 0.8] }],['svgo', {plugins: [{ removeViewBox: false },{ removeEmptyAttrs: false }]}]]}}})]
};
缓存策略
// next.config.js
module.exports = {generateBuildId: async () => {// 使用 Git commit hash 作为构建 IDreturn require('child_process').execSync('git rev-parse HEAD').toString().trim()},// 静态资源缓存配置async headers() {return [{source: '/_next/static/:path*',headers: [{key: 'Cache-Control',value: 'public, max-age=31536000, immutable'}]}]}
}
监控配置
性能监控
// utils/monitoring.ts
export const setupPerformanceMonitoring = () => {// Web Vitals 监控import('web-vitals').then(({ getCLS, getFID, getLCP }) => {getCLS(console.log);getFID(console.log);getLCP(console.log);});// 错误监控window.addEventListener('error', (event) => {console.error('Global error:', event.error);// 发送到监控服务});// 性能标记performance.mark('app-start');return () => {performance.measure('app-load', 'app-start');const measurements = performance.getEntriesByType('measure');console.log('Performance measurements:', measurements);};
};
日志配置
// utils/logger.ts
type LogLevel = 'info' | 'warn' | 'error';class Logger {private env: string;constructor() {this.env = process.env.NODE_ENV || 'development';}log(level: LogLevel, message: string, data?: any) {const timestamp = new Date().toISOString();const logData = {timestamp,level,message,data,env: this.env};if (this.env === 'production') {// 发送到日志服务fetch('/api/logs', {method: 'POST',body: JSON.stringify(logData)});} else {console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`, data);}}
}export const logger = new Logger();
安全配置
内容安全策略
// next.config.js
const securityHeaders = [{key: 'Content-Security-Policy',value: `default-src 'self';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-eval';img-src 'self' data: https:;font-src 'self';`.replace(/\s{2,}/g, ' ').trim()},{key: 'X-XSS-Protection',value: '1; mode=block'},{key: 'X-Frame-Options',value: 'SAMEORIGIN'},{key: 'X-Content-Type-Options',value: 'nosniff'}
];module.exports = {async headers() {return [{source: '/:path*',headers: securityHeaders,},]}
}
最佳实践
-
部署准备
- 环境配置完整
- 构建流程优化
- 部署脚本准备
-
性能优化
- 资源压缩
- 缓存策略
- 监控配置
-
安全考虑
- 安全头部配置
- 错误处理
- 日志记录
-
维护策略
- 自动化部署
- 监控告警
- 回滚机制