Serverless架构与前端部署:现代化部署策略

Serverless架构概述 Serverless架构让开发者无需管理服务器即可部署应用,主要优势包括: 按需付费 - 只为实际使用的资源付费 自动扩展 - 根据流量自动调整 零运维 - 无需管理服务器 全球CDN - 内容分发网络加速 Vercel部署 项目配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // vercel.json { "buildCommand": "npm run build", "outputDirectory": ".output/public", "devCommand": "npm run dev", "installCommand": "npm install", "framework": "nuxt", "regions": ["hkg1", "sin1"], "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Access-Control-Allow-Origin", "value": "*" } ] } ], "rewrites": [ { "source": "/api/:path*", "destination": "/api/:path*" } ] } 环境变量 1 2 3 4 # 在Vercel控制台设置环境变量 # 或通过vercel CLI vercel env add DATABASE_URL production vercel env add API_KEY production Serverless函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // api/hello.js export default function handler(req, res) { const { name } = req.query res.status(200).json({ message: `Hello ${name || 'World'}!`, timestamp: Date.now() }) } // api/users/[id].js export default function handler(req, res) { const { id } = req.query switch (req.method) { case 'GET': res.status(200).json({ id, name: `User ${id}` }) break case 'PUT': res.status(200).json({ id, ...req.body }) break default: res.setHeader('Allow', ['GET', 'PUT']) res.status(405).end(`Method ${req.method} Not Allowed`) } } Netlify部署 netlify.toml配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [build] command = "npm run build" publish = "dist" [build.environment] NODE_VERSION = "18" [[redirects]] from = "/api/*" to = "/.netlify/functions/:splat" status = 200 [[redirects]] from = "/*" to = "/index.html" status = 200 [functions] directory = "netlify/functions" [[plugins]] package = "@netlify/plugin-lighthouse" Netlify Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // netlify/functions/hello.js exports.handler = async (event, context) => { const { name } = event.queryStringParameters || {} return { statusCode: 200, body: JSON.stringify({ message: `Hello ${name || 'World'}!` }), headers: { 'Content-Type': 'application/json' } } } // netlify/functions/users/[id].js exports.handler = async (event, context) => { const { id } = event.pathParameters switch (event.httpMethod) { case 'GET': return { statusCode: 200, body: JSON.stringify({ id, name: `User ${id}` }) } default: return { statusCode: 405, body: 'Method Not Allowed' } } } Cloudflare Workers Worker配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // worker.js export default { async fetch(request, env, ctx) { const url = new URL(request.url) // API路由 if (url.pathname.startsWith('/api/')) { return handleAPI(request, env) } // 静态资源 return env.ASSETS.fetch(request) } } async function handleAPI(request, env) { const url = new URL(request.url) const path = url.pathname.replace('/api/', '') switch (path) { case 'hello': return new Response(JSON.stringify({ message: 'Hello from Cloudflare!' }), { headers: { 'Content-Type': 'application/json' } }) default: return new Response('Not Found', { status: 404 }) } } wrangler.toml配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 name = "my-worker" main = "worker.js" compatibility_date = "2024-01-01" [env.production] routes = [ { pattern = "https://example.com/api/*", zone_name = "example.com" } ] [env.production.vars] ENVIRONMENT = "production" API_URL = "https://api.example.com" [[env.production.kv_namespaces]] binding = "KV" id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" CI/CD配置 GitHub Actions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # .github/workflows/deploy.yml name: Deploy on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Build run: npm run build deploy-vercel: needs: test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Deploy to Vercel uses: amondnet/vercel-action@v20 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.ORG_ID }} vercel-project-id: ${{ secrets.PROJECT_ID }} vercel-args: '--prod' deploy-netlify: needs: test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Deploy to Netlify uses: nwtgck/actions-netlify@v1.2 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # .gitlab-ci.yml stages: - test - build - deploy test: stage: test image: node:18 script: - npm ci - npm test build: stage: build image: node:18 script: - npm ci - npm run build artifacts: paths: - dist deploy: stage: deploy image: node:18 script: - npm install -g vercel - vercel --prod --token=$VERCEL_TOKEN only: - main Docker部署 Dockerfile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 多阶段构建 FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 生产镜像 FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --production COPY --from=builder /app/.output ./.output EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=${DATABASE_URL} restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./ssl:/etc/nginx/ssl depends_on: - app 性能优化 CDN配置 1 2 3 4 5 6 7 8 9 10 11 12 13 // nuxt.config.ts export default defineNuxtConfig({ app: { cdnURL: 'https://cdn.example.com' }, nitro: { routeRules: { '/': { cache: { maxAge: 3600 } }, '/api/**': { cache: { maxAge: 60 } }, '/_nuxt/**': { cache: { maxAge: 31536000 } } } } }) 图片优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script setup> // 使用Nuxt Image组件 import { NuxtImg } from '#components' </script> <template> <NuxtImg src="/image.jpg" width="500" height="300" format="webp" loading="lazy" placeholder /> </template> 监控和日志 性能监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // 服务端性能追踪 export default defineEventHandler(async (event) => { const start = Date.now() // 处理请求 const result = await fetchData() const duration = Date.now() - start // 上报性能数据 $fetch('/api/analytics', { method: 'POST', body: { path: event.path, duration, timestamp: Date.now() } }) return result }) 错误追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 集成Sentry import * as Sentry from '@sentry/node' Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 0.2 }) export default defineEventHandler(async (event) => { try { return await fetchData() } catch (error) { Sentry.captureException(error) throw error } }) 最佳实践 自动化部署 - 使用CI/CD自动部署 环境隔离 - 区分开发、测试、生产环境 监控告警 - 设置性能和错误监控 备份策略 - 定期备份重要数据 灾难恢复 - 准备回滚方案 总结 Serverless架构为前端部署提供了灵活、高效的解决方案。通过合理选择平台和配置CI/CD流程,可以显著提升部署效率和应用的稳定性。 ...