在将 Tailwind CSS 项目部署到生产环境时,需要做好各项配置和优化工作。本节将详细介绍如何配置和优化部署环境。
环境配置
Docker 配置
# Dockerfile
# 构建阶段
FROM node:16-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 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["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: Deploy
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
NODE_ENV: production
- name: Deploy to server
uses: appleboy/scp-action@master
with:
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" ]; then
echo "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 作为构建 ID
return 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,
},
]
}
}
最佳实践
部署准备
- 环境配置完整
- 构建流程优化
- 部署脚本准备
性能优化
- 资源压缩
- 缓存策略
- 监控配置
安全考虑
- 安全头部配置
- 错误处理
- 日志记录
维护策略
- 自动化部署
- 监控告警
- 回滚机制