第十五章:企业级部署方案
15.1 部署前优化
生产构建配置
javascript
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// 输出目录
outDir: 'dist',
// 静态资源目录
assetsDir: 'assets',
// 压缩选项
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
},
// 代码分割
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router', 'pinia'],
'ui': ['element-plus', '@vueuse/core']
}
}
},
// 生成 sourcemap(生产环境建议 false 或 hidden)
sourcemap: false,
// chunk 大小警告
chunkSizeWarningLimit: 1000,
// 清空输出目录
emptyOutDir: true
}
})
环境变量配置
bash
# .env.production
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
VITE_SENTRY_DSN=https://xxx@sentry.io/xxx
# .env.staging
VITE_API_URL=https://staging-api.example.com
15.2 Docker 多阶段构建
Dockerfile 示例
dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 复制源码并构建
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.conf 配置
nginx
server {
listen 80;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/xml+rss
application/json;
# 静态资源缓存
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;
}
# API 代理
location /api/ {
proxy_pass https://api.example.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
构建和运行
bash
# 构建镜像
docker build -t my-app:latest .
# 运行容器
docker run -d -p 80:80 --name my-app my-app:latest
# 推送到仓库
docker tag my-app:latest my-registry.com/my-app:latest
docker push my-registry.com/my-app:latest
15.3 CDN 部署
配置 CDN 路径
javascript
// vite.config.js
export default defineConfig(({ mode }) => ({
base: mode === 'production'
? 'https://cdn.example.com/assets/'
: '/'
}))
上传到 CDN
bash
# 使用 AWS S3
aws s3 sync dist/ s3://my-cdn-bucket/assets/ --acl public-read
# 使用阿里云 OSS
ossutil cp -r dist/ oss://my-bucket/assets/ --acl public-read
15.4 CI/CD 集成
GitHub Actions 完整工作流
yaml
name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- run: npm ci
- run: npm run test:unit
- run: npm run test:e2e
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/download-artifact@v3
with:
name: dist
path: dist/
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2
with:
publish-dir: './dist'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Deploy from GitHub Actions"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
GitLab CI 配置
yaml
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
NODE_VERSION: 18
cache:
paths:
- node_modules/
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm run test:unit
artifacts:
reports:
junit: junit.xml
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache rsync openssh-client
script:
- rsync -avz -e "ssh -o StrictHostKeyChecking=no" dist/ $SSH_USER@$SSH_HOST:/var/www/html/
only:
- main
15.5 云平台部署
Vercel 部署
json
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "vite",
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
Netlify 配置
toml
# netlify.toml
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[build.environment]
NODE_VERSION = "18"
阿里云 OSS + CDN
bash
# 安装 ossutil
wget http://gosspublic.alicdn.com/ossutil/1.7.3/ossutil64
chmod 755 ossutil64
# 配置
./ossutil64 config
# 上传
./ossutil64 cp -r dist/ oss://my-bucket/ --update --delete
# 刷新 CDN
./ossutil64 cdn-refresh --paths "https://cdn.example.com/*"
15.6 监控和告警
Sentry 集成
bash
npm install @sentry/vue @sentry/tracing
javascript
// src/main.js
import * as Sentry from '@sentry/vue'
Sentry.init({
app,
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
tracesSampleRate: 0.1,
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router)
})
]
})
Web Vitals 监控
javascript
// 上报性能指标
import { getCLS, getFID, getLCP, getFCP, getTTFB } from 'web-vitals'
function sendToAnalytics(metric) {
// 发送到监控平台
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating
})
})
}
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getLCP(sendToAnalytics)
getFCP(sendToAnalytics)
getTTFB(sendToAnalytics)
15.7 回滚策略
版本化部署
bash
# 构建时生成版本标识
echo $(git rev-parse --short HEAD) > dist/version.txt
# 部署时保留历史版本
rsync -avz dist/ /var/www/releases/$(date +%Y%m%d_%H%M%S)/
ln -snf /var/www/releases/$(date +%Y%m%d_%H%M%S) /var/www/current
快速回滚
bash
# 切换到上一版本
cd /var/www/releases
ls -t | tail -n 2 | head -n 1 | xargs -I {} ln -snf {} /var/www/current
本章小结
企业级部署的核心要点:
- 构建优化:压缩、代码分割、环境变量
- Docker 容器化:多阶段构建、nginx 配置
- CDN 加速:静态资源分发
- CI/CD 自动化:GitHub Actions、GitLab CI
- 监控告警:Sentry、Web Vitals
- 版本管理和回滚:保障稳定