乐闻世界logo
搜索文章和话题

面试题手册

Vercel 的 Edge Functions 和 Serverless Functions 有什么区别?

Vercel 的 Edge Functions 和 Serverless Functions 有什么区别?Vercel 提供了两种主要的计算服务:Edge Functions 和 Serverless Functions。虽然它们都用于运行后端代码,但在架构、性能、使用场景等方面有显著差异。理解这些区别对于选择合适的计算服务至关重要。Serverless Functions定义和架构什么是 Serverless Functions:运行在 Vercel 的无服务器计算平台上基于 AWS Lambda 或类似技术支持长时间运行的计算任务适合处理复杂的业务逻辑架构特点:分布式服务器集群冷启动时间较长(几百毫秒到几秒)更长的执行时间限制更大的内存配额支持完整的 Node.js 运行时特性执行环境:Node.js 运行时支持所有 Node.js 模块完整的文件系统访问(只读)支持数据库连接支持外部 API 调用性能特点:执行时间:最长 60 秒(Pro 计划)内存:最高 3008 MB(Pro 计划)冷启动:较慢(500ms - 3s)并发:受限于计划配额使用场景:复杂的数据处理长时间运行的 API数据库操作文件处理第三方 API 集成代码示例// pages/api/hello.jsexport default async function handler(req, res) { // 复杂的数据处理 const data = await fetchComplexData(); // 数据库操作 const result = await database.query(data); // 文件处理 const processed = await processFile(result); res.status(200).json({ data: processed });}export const config = { maxDuration: 30, // 30 秒执行时间 memory: 2048, // 2GB 内存};Edge Functions定义和架构什么是 Edge Functions:运行在 Vercel 的全球边缘网络上基于 V8 JavaScript 引擎极快的响应时间适合处理轻量级、低延迟的任务架构特点:全球分布的边缘节点极快的冷启动时间(几毫秒)更短的执行时间限制较小的内存配额受限的运行时环境特性执行环境:Edge Runtime(基于 V8)支持标准的 Web API受限的 Node.js API不支持文件系统访问不支持数据库连接池性能特点:执行时间:最长 30 秒内存:128 MB冷启动:极快(< 50ms)并发:高并发能力使用场景:请求路由和重定向A/B 测试地理位置路由认证和授权内容个性化缓存控制代码示例// middleware.jsimport { NextResponse } from 'next/server';import type { NextRequest } from 'next/server';export const runtime = 'edge';export function middleware(request: NextRequest) { // 极快的响应 const geo = request.geo; // 地理位置路由 if (geo?.country === 'CN') { return NextResponse.rewrite(new URL('/zh', request.url)); } // A/B 测试 const abTest = Math.random() > 0.5 ? 'A' : 'B'; const response = NextResponse.next(); response.cookies.set('ab-test', abTest); return response;}export const config = { matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',};详细对比1. 性能对比| 特性 | Serverless Functions | Edge Functions ||------|-------------------|----------------|| 冷启动时间 | 500ms - 3s | < 50ms || 执行时间限制 | 60s (Pro) | 30s || 内存限制 | 3008 MB (Pro) | 128 MB || 响应延迟 | 中等 | 极低 || 并发能力 | 中等 | 高 |2. 运行时对比| 特性 | Serverless Functions | Edge Functions ||------|-------------------|----------------|| 运行时 | Node.js | Edge Runtime (V8) || Node.js API | 完整支持 | 受限支持 || Web API | 支持 | 完整支持 || 文件系统 | 只读访问 | 不支持 || 数据库连接 | 支持 | 受限 |3. 使用场景对比| 场景 | Serverless Functions | Edge Functions ||------|-------------------|----------------|| 复杂计算 | ✅ 适合 | ❌ 不适合 || 数据处理 | ✅ 适合 | ❌ 不适合 || 请求路由 | ⚠️ 可以 | ✅ 最佳 || A/B 测试 | ⚠️ 可以 | ✅ 最佳 || 认证授权 | ⚠️ 可以 | ✅ 最佳 || 文件处理 | ✅ 适合 | ❌ 不适合 || API 集成 | ✅ 适合 | ⚠️ 受限 |选择指南选择 Serverless Functions 的场景1. 需要长时间运行的任务// 处理大型数据集export default async function handler(req, res) { const largeDataset = await fetchLargeDataset(); const processed = await processLargeData(largeDataset); res.status(200).json({ data: processed });}export const config = { maxDuration: 60, // 需要更长的执行时间};2. 需要数据库操作import { PrismaClient } from '@prisma/client';const prisma = new PrismaClient();export default async function handler(req, res) { // 复杂的数据库查询 const users = await prisma.user.findMany({ include: { posts: true, comments: true, }, }); res.status(200).json({ users });}3. 需要文件处理import formidable from 'formidable';export default async function handler(req, res) { const form = formidable({ multiples: true }); const [fields, files] = await form.parse(req); // 处理上传的文件 const processed = await processFiles(files); res.status(200).json({ processed });}4. 需要完整的 Node.js APIimport fs from 'fs/promises';import path from 'path';export default async function handler(req, res) { // 使用 Node.js 文件系统 const filePath = path.join(process.cwd(), 'data.json'); const data = await fs.readFile(filePath, 'utf-8'); res.status(200).json(JSON.parse(data));}选择 Edge Functions 的场景1. 需要极快响应的路由// middleware.jsimport { NextResponse } from 'next/server';export const runtime = 'edge';export function middleware(request: NextRequest) { // 极快的路由决策 if (request.nextUrl.pathname.startsWith('/api')) { return NextResponse.rewrite(new URL('/api-handler', request.url)); } return NextResponse.next();}2. 需要地理位置路由export function middleware(request: NextRequest) { const geo = request.geo; // 基于地理位置的路由 if (geo?.country === 'US') { return NextResponse.rewrite(new URL('/us', request.url)); } else if (geo?.country === 'CN') { return NextResponse.rewrite(new URL('/zh', request.url)); } return NextResponse.next();}3. 需要 A/B 测试export function middleware(request: NextRequest) { // A/B 测试逻辑 const variant = Math.random() > 0.5 ? 'A' : 'B'; const response = NextResponse.next(); response.cookies.set('ab-variant', variant); // 根据变体修改响应 if (variant === 'B') { response.headers.set('X-Experiment', 'B'); } return response;}4. 需要认证和授权export function middleware(request: NextRequest) { const token = request.cookies.get('auth-token'); // 快速认证检查 if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } // 验证 token const isValid = verifyToken(token.value); if (!isValid) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next();}混合使用策略1. 分层架构Edge Functions 处理路由和认证:// middleware.jsexport function middleware(request: NextRequest) { // 快速路由和认证 if (!isAuthenticated(request)) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next();}Serverless Functions 处理业务逻辑:// pages/api/data.jsexport default async function handler(req, res) { // 复杂的业务逻辑 const data = await processBusinessLogic(req.body); res.status(200).json({ data });}2. 缓存策略Edge Functions 处理缓存:export function middleware(request: NextRequest) { const cacheKey = request.url; // 检查缓存 const cached = cache.get(cacheKey); if (cached) { return new Response(cached, { headers: { 'X-Cache': 'HIT' } }); } return NextResponse.next();}Serverless Functions 生成数据:export default async function handler(req, res) { // 生成数据 const data = await generateData(); // 设置缓存头 res.setHeader('Cache-Control', 'public, max-age=3600'); res.status(200).json(data);}性能优化建议1. Serverless Functions 优化减少冷启动:保持函数热状态使用连接池优化依赖加载使用预编译优化执行时间:避免长时间运行的任务使用异步处理优化数据库查询使用缓存2. Edge Functions 优化极简代码:只包含必要的逻辑避免复杂的计算使用轻量级依赖优化代码大小利用缓存:使用 KV 存储实现智能缓存设置合理的缓存时间使用 CDN 缓存成本考虑1. Serverless Functions 成本计费方式:按执行时间计费按内存使用计费按请求次数计费成本优化:优化执行时间减少内存使用实现缓存策略批量处理请求2. Edge Functions 成本计费方式:按请求次数计费按执行时间计费成本优化:减少不必要的 Edge Functions优化代码逻辑使用缓存减少调用合理使用路由规则最佳实践1. 架构设计分层设计:Edge Functions:路由、认证、缓存Serverless Functions:业务逻辑、数据处理数据库:数据存储和查询职责分离:每个函数只做一件事避免过度复杂便于测试和维护2. 监控和调试性能监控:监控执行时间监控冷启动时间监控错误率监控资源使用日志记录:记录关键操作记录错误信息记录性能指标使用结构化日志3. 测试策略单元测试:测试函数逻辑测试边界条件测试错误处理集成测试:测试函数间交互测试数据库集成测试 API 集成性能测试:测试响应时间测试并发能力测试冷启动时间总结Serverless Functions 适合:复杂的业务逻辑长时间运行的任务数据库操作文件处理需要完整 Node.js APIEdge Functions 适合:极快响应的路由地理位置路由A/B 测试认证和授权内容个性化缓存控制最佳实践:根据需求选择合适的函数类型混合使用两种函数类型实现分层架构优化性能和成本持续监控和改进通过理解 Serverless Functions 和 Edge Functions 的区别,开发者可以更好地设计应用架构,选择合适的计算服务,实现最佳的性能和成本效益。
阅读 0·2月21日 16:45

什么是CDN?CDN的工作原理是什么?

核心概念CDN(Content Delivery Network,内容分发网络)是一组分布在多个地理位置的服务器,它们共同工作以提供快速、高可用性和安全的互联网内容传输服务。CDN 的主要目标是通过将内容存储在靠近最终用户的边缘服务器上,来减少网络延迟和带宽消耗,从而提升用户体验。工作原理当用户访问使用 CDN 的网站时,CDN 会根据用户的地理位置、网络状况和服务器负载等因素,智能地将用户的请求路由到最近的边缘节点。这个过程包括:DNS 解析:用户访问网站时,DNS 服务器会返回 CDN 节点的 IP 地址内容缓存:CDN 边缘节点检查是否有缓存的内容回源请求:如果缓存未命中,边缘节点会向源站请求内容内容分发:获取内容后,边缘节点会缓存该内容并返回给用户后续请求:后续相同内容的请求直接从边缘节点返回关键技术缓存策略TTL(Time To Live):控制内容在 CDN 节点的缓存时间缓存键:根据 URL、请求头等生成唯一标识缓存层级:边缘缓存、区域缓存、源站缓存负载均衡地理位置路由:根据用户位置选择最近的节点健康检查:监控节点状态,自动剔除故障节点流量调度:根据节点负载动态分配流量安全机制DDoS 防护:分布式防御大规模攻击WAF(Web Application Firewall):过滤恶意请求HTTPS 支持:提供 SSL/TLS 加密传输优势降低延迟:用户从就近节点获取内容,减少传输距离减轻源站压力:大部分请求由 CDN 节点处理提高可用性:分布式架构提供冗余保障节省带宽成本:减少源站带宽消耗提升安全性:提供额外的安全防护层应用场景静态资源加速:图片、CSS、JavaScript 等静态文件视频流媒体:直播、点播等视频内容分发软件分发:应用下载、更新包分发API 加速:API 响应缓存和加速全站加速:动态和静态内容综合加速常见 CDN 服务商Cloudflare:全球覆盖,提供免费套餐Akamai:行业领先,企业级解决方案AWS CloudFront:与 AWS 服务集成紧密阿里云 CDN:国内覆盖广泛腾讯云 CDN:游戏加速优势明显面试要点在回答这个问题时,应该重点突出:CDN 的定义和核心目标工作原理的清晰描述关键技术点的理解实际应用场景的举例与传统直接访问的区别
阅读 0·2月21日 16:28

RxJS 中的 Observable 和 Promise 有什么区别?

核心区别Observable 和 Promise 都是处理异步操作的工具,但它们在设计理念和使用方式上有显著差异:1. 执行时机Promise: 一旦创建就会立即执行,无法取消const promise = new Promise((resolve) => { console.log('Promise 立即执行'); resolve('done');});Observable: 只有订阅时才会执行,可以取消订阅const observable = new Observable((observer) => { console.log('Observable 订阅时执行'); observer.next('data');});observable.subscribe();2. 数据流Promise: 只能发出一个值,然后完成或失败promise.then(value => console.log(value)); // 只接收一个值Observable: 可以发出多个值,随时间推移持续推送数据observable.subscribe(value => console.log(value)); // 可以接收多个值3. 可取消性Promise: 无法取消,一旦创建就会执行到底const promise = fetch('/api/data');// 无法中途取消这个请求Observable: 可以通过 unsubscribe() 取消订阅const subscription = observable.subscribe();subscription.unsubscribe(); // 取消订阅4. 操作符支持Promise: 只有链式调用 then/catch/finallypromise .then(data => processData(data)) .then(result => console.log(result)) .catch(error => handleError(error));Observable: 丰富的操作符生态系统observable .pipe( map(data => processData(data)), filter(result => result.isValid), catchError(error => handleError(error)) ) .subscribe();5. 懒加载 vs 急切加载Promise: 急切执行,创建时就开始工作Observable: 懒加载,直到订阅才开始执行6. 同步/异步Promise: 总是异步的Observable: 可以是同步或异步的实际应用场景使用 Promise 的场景单次异步操作不需要取消只需要一个结果简单的异步流程使用 Observable 的场景需要处理多个值需要取消操作复杂的数据流处理事件处理WebSocket 连接实时数据流性能考虑Observable 在处理复杂异步流程时提供了更好的性能和灵活性,特别是在需要处理多个异步操作或需要取消操作的场景中。Promise 更适合简单的异步操作,代码更简洁直观。
阅读 0·2月21日 16:28

RxJS 中常用的操作符有哪些?如何使用?

常用操作符分类1. 创建操作符 (Creation Operators)of创建一个发出指定值的 Observableimport { of } from 'rxjs';of(1, 2, 3).subscribe(console.log);// 输出: 1, 2, 3from将数组、Promise、Iterable 等转换为 Observableimport { from } from 'rxjs';from([1, 2, 3]).subscribe(console.log);// 输出: 1, 2, 3from(Promise.resolve('Hello')).subscribe(console.log);// 输出: Hellointerval / timer创建定时发出的 Observableimport { interval, timer } from 'rxjs';interval(1000).subscribe(console.log);// 每秒发出一个递增数字: 0, 1, 2, 3...timer(2000, 1000).subscribe(console.log);// 2秒后开始,每秒发出一个数字: 0, 1, 2, 3...2. 转换操作符 (Transformation Operators)map转换每个发出的值import { of } from 'rxjs';import { map } from 'rxjs/operators';of(1, 2, 3).pipe( map(x => x * 2)).subscribe(console.log);// 输出: 2, 4, 6switchMap取消之前的内部 Observable,只处理最新的import { fromEvent } from 'rxjs';import { switchMap } from 'rxjs/operators';fromEvent(document, 'click').pipe( switchMap(() => fetch('/api/data'))).subscribe(response => console.log(response));mergeMap并行处理所有内部 Observableimport { of } from 'rxjs';import { mergeMap } from 'rxjs/operators';of(1, 2, 3).pipe( mergeMap(x => of(x, x * 2))).subscribe(console.log);// 输出: 1, 2, 2, 4, 3, 6concatMap顺序处理内部 Observable,一个完成后再处理下一个import { of } from 'rxjs';import { concatMap } from 'rxjs/operators';of(1, 2, 3).pipe( concatMap(x => of(x, x * 2))).subscribe(console.log);// 输出: 1, 2, 2, 4, 3, 6 (顺序执行)3. 过滤操作符 (Filtering Operators)filter过滤符合条件的值import { of } from 'rxjs';import { filter } from 'rxjs/operators';of(1, 2, 3, 4, 5).pipe( filter(x => x % 2 === 0)).subscribe(console.log);// 输出: 2, 4take / takeLast / takeUntil只取前几个、后几个或直到某个条件import { interval, fromEvent } from 'rxjs';import { take, takeUntil } from 'rxjs/operators';interval(1000).pipe( take(3)).subscribe(console.log);// 输出: 0, 1, 2interval(1000).pipe( takeUntil(fromEvent(document, 'click'))).subscribe(console.log);// 点击时停止debounceTime在指定时间内只发出最后一个值import { fromEvent } from 'rxjs';import { debounceTime } from 'rxjs/operators';fromEvent(inputElement, 'input').pipe( debounceTime(300)).subscribe(event => console.log(event.target.value));throttleTime在指定时间内只发出第一个值import { fromEvent } from 'rxjs';import { throttleTime } from 'rxjs/operators';fromEvent(window, 'scroll').pipe( throttleTime(200)).subscribe(event => console.log('Scroll event'));4. 组合操作符 (Combination Operators)merge合并多个 Observable,并行发出值import { merge, interval } from 'rxjs';merge( interval(1000).pipe(map(x => `A${x}`)), interval(1500).pipe(map(x => `B${x}`))).subscribe(console.log);concat顺序连接多个 Observableimport { concat, of } from 'rxjs';concat( of(1, 2), of(3, 4), of(5, 6)).subscribe(console.log);// 输出: 1, 2, 3, 4, 5, 6combineLatest组合多个 Observable 的最新值import { combineLatest, of } from 'rxjs';combineLatest([ of(1, 2, 3), of('a', 'b', 'c')]).subscribe(([num, char]) => console.log(num, char));// 输出: [3, 'a'], [3, 'b'], [3, 'c']zip按索引组合多个 Observable 的值import { zip, of } from 'rxjs';zip( of(1, 2, 3), of('a', 'b', 'c')).subscribe(([num, char]) => console.log(num, char));// 输出: [1, 'a'], [2, 'b'], [3, 'c']5. 错误处理操作符 (Error Handling Operators)catchError捕获错误并返回新的 Observableimport { of } from 'rxjs';import { map, catchError } from 'rxjs/operators';of(1, 2, 3, 4).pipe( map(x => { if (x === 3) throw new Error('Error!'); return x; }), catchError(error => of('default value'))).subscribe(console.log);// 输出: 1, 2, 'default value'retry重试失败的 Observableimport { of } from 'rxjs';import { map, retry } from 'rxjs/operators';let count = 0;of(1, 2, 3).pipe( map(x => { count++; if (count < 3) throw new Error('Error!'); return x; }), retry(2)).subscribe(console.log);6. 工具操作符 (Utility Operators)tap执行副作用,不修改值import { of } from 'rxjs';import { tap, map } from 'rxjs/operators';of(1, 2, 3).pipe( tap(x => console.log('Before:', x)), map(x => x * 2), tap(x => console.log('After:', x))).subscribe();delay延迟发出值import { of } from 'rxjs';import { delay } from 'rxjs/operators';of(1, 2, 3).pipe( delay(1000)).subscribe(console.log);// 1秒后输出: 1, 2, 3实际应用示例搜索框防抖fromEvent(searchInput, 'input').pipe( debounceTime(300), map(event => event.target.value), filter(query => query.length > 2), switchMap(query => searchAPI(query))).subscribe(results => displayResults(results));自动保存form.valueChanges.pipe( debounceTime(1000), distinctUntilChanged(), switchMap(formData => saveAPI(formData))).subscribe();并行请求merge( getUserData(userId), getUserPosts(userId), getUserComments(userId)).pipe( combineLatestAll()).subscribe(([user, posts, comments]) => { renderUserProfile(user, posts, comments);});最佳实践合理使用 pipe() 链式调用注意操作符的执行顺序及时取消订阅避免内存泄漏根据场景选择合适的操作符使用 TypeScript 获得更好的类型推断
阅读 0·2月21日 16:28

如何在 Vercel 上配置自定义域名和 SSL 证书?

如何在 Vercel 上配置自定义域名和 SSL 证书?在 Vercel 上配置自定义域名和 SSL 证书是一个简单直接的过程,Vercel 提供了自动化的 SSL 证书管理,使得这一过程变得非常便捷。自定义域名配置1. 添加自定义域名通过 Vercel Dashboard 添加:登录 Vercel Dashboard选择你的项目进入 "Settings" → "Domains"点击 "Add Domain"输入你的域名(如 example.com 或 www.example.com)选择域名类型:Production:用于生产环境Preview:用于预览部署通过 Vercel CLI 添加:# 添加生产域名vercel domains add example.com# 添加预览域名vercel domains add preview.example.com --preview2. 域名类型Vercel 支持以下域名类型:根域名:example.com需要配置 A 记录或 CNAME 别名记录子域名:www.example.comblog.example.comapi.example.com使用 CNAME 记录通配符域名:*.example.com匹配所有子域名需要付费计划3. DNS 配置配置步骤:在 Vercel Dashboard 中查看 DNS 配置要求登录你的域名注册商(如 GoDaddy、Namecheap、Cloudflare)根据类型添加相应的 DNS 记录A 记录配置(用于根域名):Type: AName: @Value: 76.76.21.21TTL: 3600 (或默认值)CNAME 记录配置(用于子域名):Type: CNAMEName: wwwValue: cname.vercel-dns.comTTL: 3600 (或默认值)使用 Cloudflare 时的特殊配置:如果你使用 Cloudflare 作为 DNS 提供商,需要注意:代理模式:启用 Cloudflare 代理(橙色云朵)时,Vercel 可能无法正确验证域名所有权建议先禁用代理(灰色云朵),等待验证通过后再启用SSL 模式:设置为 "Full" 或 "Full (strict)"不要使用 "Flexible" 模式4. 域名验证验证过程:添加 DNS 记录后,Vercel 会自动验证域名配置验证通常需要几分钟到几小时(取决于 DNS 传播)验证成功后,域名状态会显示为 "Valid Configuration"验证检查:# 检查 DNS 记录是否生效dig example.com# 检查 CNAME 记录dig www.example.com CNAME# 检查 DNS 传播nslookup example.comSSL 证书配置1. 自动 SSL 证书Vercel 使用 Let's Encrypt 自动为你的域名提供 SSL 证书:特点:完全免费自动续期支持 HTTP/2 和 HTTP/3自动配置证书类型:单域名证书:保护单个域名通配符证书:保护主域名及其所有子域名(需要付费计划)2. SSL 证书自动管理自动颁发:域名验证通过后,Vercel 自动颁发 SSL 证书通常在几分钟内完成无需手动操作自动续期:证书到期前 30 天自动续期无需担心证书过期完全自动化证书监控:Vercel 持续监控证书状态发现问题时自动修复在 Dashboard 中可以查看证书详情3. 强制 HTTPS自动重定向:Vercel 默认将所有 HTTP 请求重定向到 HTTPS:// 在 vercel.json 中配置{ "redirects": [ { "source": "/:path*", "has": [ { "type": "host", "value": "example.com" } ], "destination": "https://example.com/:path*", "permanent": true } ]}HSTS 配置:{ "headers": [ { "source": "/(.*)", "headers": [ { "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" } ] } ]}高级配置1. 多域名配置添加多个域名:在 Domains 设置中逐个添加域名为每个域名配置 DNS 记录等待所有域名验证通过主域名设置:// 在 vercel.json 中设置主域名{ "name": "my-project", "alias": ["example.com", "www.example.com"]}2. 环境特定域名预览环境域名:# 为预览部署添加特定域名vercel domains add preview.example.com --preview开发环境域名:# 为开发环境添加域名vercel domains add dev.example.com --dev3. 自定义 404 页面配置自定义 404 页面:// 在 vercel.json 中配置{ "rewrites": [ { "source": "/404", "destination": "/custom-404.html" } ], "notFound": true}故障排除1. 域名验证失败常见原因:DNS 记录配置错误DNS 传播未完成域名已被其他服务使用Cloudflare 代理模式问题解决方案:检查 DNS 记录是否正确使用 DNS 检查工具验证记录等待 DNS 传播完成(最多 48 小时)如果使用 Cloudflare,暂时禁用代理# 检查 DNS 记录dig example.com +short# 检查 WHOIS 信息whois example.com# 检查 DNS 传播dig example.com @8.8.8.82. SSL 证书颁发失败常见原因:域名验证未通过DNS 记录配置错误防火墙阻止 Let's Encrypt 验证域名已被其他证书使用解决方案:确保域名验证通过检查 DNS 记录配置检查防火墙设置在 Dashboard 中手动触发证书重新颁发3. HTTPS 重定向不工作检查清单:确认 SSL 证书已颁发检查 Vercel 配置中的重定向设置清除浏览器缓存使用隐私模式测试最佳实践1. 域名管理使用版本控制管理域名配置定期检查域名到期时间为重要域名设置自动续费保留域名访问权限的备份2. SSL 安全启用 HSTS 以强制 HTTPS定期检查 SSL 证书状态使用强密码保护域名账户启用双因素认证3. 监控和告警设置域名到期提醒监控 SSL 证书状态配置网站可用性监控设置错误告警4. 性能优化使用 CDN 加速静态资源启用 HTTP/2 和 HTTP/3优化 DNS 解析时间使用现代 TLS 版本常见问题解答Q1: 可以使用免费域名吗?A: 可以,Vercel 支持任何合法的域名,包括免费域名。但建议使用付费域名以获得更好的稳定性和支持。Q2: SSL 证书有数量限制吗?A: 免费计划中,每个项目可以添加无限数量的域名,每个域名都会自动获得 SSL 证书。Q3: 如何迁移现有域名到 Vercel?A: 在 Vercel 中添加域名更新 DNS 记录指向 Vercel等待 DNS 传播验证配置正确Q4: 可以使用自己的 SSL 证书吗?A: Vercel 不支持上传自定义 SSL 证书,必须使用 Vercel 自动颁发的 Let's Encrypt 证书。Q5: 域名配置需要多长时间生效?A: 通常需要几分钟到几小时,最长可能需要 48 小时(DNS 传播时间)。总结在 Vercel 上配置自定义域名和 SSL 证书的主要优势:自动化:SSL 证书自动颁发和续期简单:直观的配置界面免费:Let's Encrypt SSL 证书完全免费安全:自动 HTTPS 重定向可靠:99.99% 的正常运行时间保证通过遵循上述步骤和最佳实践,你可以轻松地在 Vercel 上配置自定义域名和 SSL 证书,为你的网站提供安全、专业的访问体验。
阅读 0·2月21日 16:28

如何在 Vercel 上实现多环境部署(开发、测试、生产)?

如何在 Vercel 上实现多环境部署(开发、测试、生产)?在 Vercel 上实现多环境部署是现代软件开发的重要实践,它允许开发者在不同的环境中测试和部署代码,确保代码质量和稳定性。以下是详细的实现指南。Vercel 环境概念1. 三种主要环境Production(生产环境):部署到主域名使用生产数据库和 APIURL: https://your-project.vercel.app 或自定义域名触发条件:合并到主分支Preview(预览环境):为每个分支或 PR 生成唯一 URL使用测试数据库和 APIURL: https://your-project-branch.vercel.app触发条件:创建或更新 Pull RequestDevelopment(开发环境):本地开发使用使用开发数据库和 API通过 Vercel CLI 访问用于本地测试和调试环境配置1. 环境变量管理在 Dashboard 中配置:进入项目设置选择 "Environment Variables"添加环境变量选择适用的环境(Production、Preview、Development)配置示例:| 变量名 | Production | Preview | Development ||--------|-----------|---------|-------------|| DATABASE_URL | postgres://prod-db... | postgres://test-db... | postgres://dev-db... || API_URL | https://api.example.com | https://api-test.example.com | http://localhost:3001 || NODE_ENV | production | preview | development || SENTRY_DSN | prod-dsn | test-dsn | dev-dsn |通过 CLI 配置:# 添加生产环境变量vercel env add DATABASE_URL production# 输入生产数据库 URL# 添加预览环境变量vercel env add DATABASE_URL preview# 输入测试数据库 URL# 添加开发环境变量vercel env add DATABASE_URL development# 输入开发数据库 URL2. 环境特定配置使用 vercel.json:{ "version": 2, "buildCommand": "npm run build", "outputDirectory": "dist", "env": { "BUILD_TIME": "${NOW}" }, "build": { "env": { "BUILD_ENV": "production" } }}在代码中使用环境变量:// 获取环境const environment = process.env.NODE_ENV || 'development';// 根据环境配置const config = { production: { apiUrl: 'https://api.example.com', databaseUrl: process.env.DATABASE_URL, enableAnalytics: true, }, preview: { apiUrl: 'https://api-test.example.com', databaseUrl: process.env.DATABASE_URL, enableAnalytics: false, }, development: { apiUrl: 'http://localhost:3001', databaseUrl: 'postgres://localhost:5432/dev', enableAnalytics: false, },};const currentConfig = config[environment] || config.development;分支策略1. Git Flow 分支模型分支结构:main (生产环境) └── develop (开发环境) ├── feature/user-authentication ├── feature/payment-gateway └── bugfix/login-error配置部署规则:// vercel.json{ "git": { "deploymentEnabled": { "main": true, "develop": true, "feature/*": true, "hotfix/*": true } }}2. Trunk-Based Development分支结构:main (主分支) ├── feature-branch-1 ├── feature-branch-2 └── hotfix-branch配置:{ "git": { "deploymentEnabled": { "main": true, "feature-*": true, "hotfix-*": true } }}部署流程1. 开发环境部署本地开发:# 安装 Vercel CLInpm install -g vercel# 登录vercel login# 拉取环境变量vercel env pull .env.local# 启动本地开发服务器vercel dev# 或使用 npm scriptsnpm run dev开发环境配置:// .env.localDATABASE_URL=postgres://localhost:5432/devAPI_URL=http://localhost:3001NODE_ENV=development2. 预览环境部署创建 Pull Request:创建功能分支推送代码到远程仓库创建 Pull RequestVercel 自动创建预览部署获取预览 URL 进行测试预览部署 URL:格式:https://your-project-branch-name.vercel.app示例:https://myapp-feature-auth.vercel.app配置预览环境:// vercel.json{ "preview": { "env": { "PREVIEW_MODE": "true" } }}3. 生产环境部署合并到主分支:代码审查通过合并 Pull Request 到主分支Vercel 自动触发生产部署部署到生产域名手动触发生产部署:# 部署到生产环境vercel --prod# 或使用特定分支vercel --prod --scope your-team生产环境配置:// vercel.json{ "production": { "env": { "PRODUCTION_MODE": "true" } }}数据库管理1. 多环境数据库使用不同的数据库:// lib/database.jsconst { PrismaClient } = require('@prisma/client');let prisma;if (process.env.NODE_ENV === 'production') { prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL, }, }, });} else if (process.env.NODE_ENV === 'preview') { prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL, }, }, });} else { prisma = new PrismaClient({ datasources: { db: { url: 'postgres://localhost:5432/dev', }, }, });}module.exports = prisma;2. 数据库迁移环境特定的迁移:# 开发环境迁移npm run migrate:dev# 预览环境迁移npm run migrate:preview# 生产环境迁移npm run migrate:prod配置迁移脚本:{ "scripts": { "migrate:dev": "prisma migrate dev", "migrate:preview": "prisma migrate deploy", "migrate:prod": "prisma migrate deploy" }}测试策略1. 环境特定测试开发环境测试:// tests/setup.jsconst { execSync } = require('child_process');if (process.env.NODE_ENV === 'development') { // 设置测试数据库 execSync('npm run db:setup:test'); // 运行单元测试 execSync('npm run test:unit');}预览环境测试:// tests/integration.jsdescribe('Integration Tests', () => { beforeAll(() => { // 跳过预览环境的某些测试 if (process.env.NODE_ENV === 'preview') { console.log('Skipping expensive integration tests in preview'); } }); test('API integration', async () => { // 集成测试 });});生产环境测试:// tests/smoke.jsdescribe('Smoke Tests', () => { test('Production health check', async () => { if (process.env.NODE_ENV === 'production') { const response = await fetch('https://your-project.vercel.app/health'); expect(response.status).toBe(200); } });});2. 自动化测试配置 CI/CD:# .github/workflows/test.ymlname: Teston: push: branches: [main, develop] pull_request: branches: [main]jobs: 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 test - run: npm run lint监控和日志1. 环境特定监控配置监控:// lib/monitoring.jsconst Sentry = require('@sentry/node');if (process.env.NODE_ENV === 'production') { Sentry.init({ dsn: process.env.SENTRY_DSN, environment: 'production', tracesSampleRate: 1.0, });} else if (process.env.NODE_ENV === 'preview') { Sentry.init({ dsn: process.env.SENTRY_DSN, environment: 'preview', tracesSampleRate: 0.5, });} else { // 开发环境不启用 Sentry console.log('Monitoring disabled in development');}2. 日志管理环境特定日志:// lib/logger.jsconst winston = require('winston');const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.json(), transports: [ new winston.transports.Console({ format: winston.format.simple(), }), ],});if (process.env.NODE_ENV === 'production') { logger.add(new winston.transports.File({ filename: 'error.log', level: 'error' })); logger.add(new winston.transports.File({ filename: 'combined.log' }));}module.exports = logger;最佳实践1. 环境隔离严格的环境隔离:每个环境使用独立的数据库每个环境使用独立的 API 端点每个环境使用独立的存储避免环境间数据混淆环境命名规范:使用清晰的环境名称在代码中明确标识环境在日志中记录环境信息2. 配置管理使用配置文件:// config/index.jsconst config = { production: { apiUrl: process.env.API_URL, databaseUrl: process.env.DATABASE_URL, enableAnalytics: true, logLevel: 'error', }, preview: { apiUrl: process.env.API_URL, databaseUrl: process.env.DATABASE_URL, enableAnalytics: false, logLevel: 'warn', }, development: { apiUrl: 'http://localhost:3001', databaseUrl: 'postgres://localhost:5432/dev', enableAnalytics: false, logLevel: 'debug', },};const environment = process.env.NODE_ENV || 'development';module.exports = config[environment];3. 安全性环境变量安全:不要在代码中硬编码敏感信息使用环境变量存储密钥定期轮换密钥限制环境变量访问权限生产环境保护:限制生产环境访问使用强密码和密钥启用双因素认证定期审计访问日志4. 部署策略渐进式部署:先部署到预览环境进行充分测试部署到生产环境监控生产环境如有问题,快速回滚回滚策略:保留所有历史部署快速回滚到稳定版本记录回滚原因分析问题根源故障排除1. 环境变量问题问题:环境变量未生效解决方案:检查环境变量是否正确配置确认环境变量名称拼写正确重新部署项目检查环境变量适用的环境2. 部署失败问题:特定环境部署失败解决方案:查看部署日志检查环境变量配置验证依赖安装检查构建命令3. 数据库连接问题问题:无法连接到数据库解决方案:验证数据库 URL 配置检查数据库访问权限确认数据库服务运行状态测试数据库连接总结在 Vercel 上实现多环境部署的关键点:环境隔离:每个环境使用独立的资源和配置环境变量:正确配置和管理环境变量分支策略:选择合适的 Git 分支模型自动化:利用 CI/CD 自动化部署流程监控:实施全面的监控和日志记录安全性:保护生产环境和敏感信息测试:在每个环境中进行充分测试通过遵循这些最佳实践,可以在 Vercel 上建立可靠、高效的多环境部署流程,提高代码质量和部署效率。
阅读 0·2月21日 16:28

如何优化 Vercel 应用的性能?

如何优化 Vercel 应用的性能?优化 Vercel 应用的性能是一个多方面的任务,涉及前端构建、资源加载、服务器端渲染、缓存策略等多个层面。以下是从不同角度优化 Vercel 应用性能的详细指南。构建优化1. 代码分割自动代码分割:Next.js 和现代前端框架会自动进行代码分割,但你可以进一步优化:// 动态导入组件const HeavyComponent = dynamic(() => import('./HeavyComponent'), { loading: () => <LoadingSpinner />, ssr: false // 禁用服务端渲染});export default function Page() { return <HeavyComponent />;}路由级别的分割:每个路由自动分割成独立的 chunk只加载当前路由需要的代码利用 Next.js 的自动分割2. Tree Shaking移除未使用的代码:// 避免导入整个库// ❌ 不好import _ from 'lodash';// ✅ 好import { debounce } from 'lodash';使用 ES Modules:确保使用 ES Module 语法配置 package.json 的 "type": "module"使用支持 tree shaking 的库3. 依赖优化分析包大小:# 使用 webpack-bundle-analyzernpm install --save-dev @next/bundle-analyzer// next.config.jsconst withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true',});module.exports = withBundleAnalyzer({ // 其他配置});优化策略:移除不必要的依赖使用更轻量的替代库按需导入大型库资源加载优化1. 图片优化使用 next/image:import Image from 'next/image';export default function Hero() { return ( <Image src="/hero.jpg" alt="Hero section" width={800} height={600} priority // 首屏图片 placeholder="blur" // 模糊占位符 /> );}优化技巧:使用 WebP、AVIF 等现代格式提供正确的尺寸使用 priority 属性加载首屏图片使用 placeholder 提升用户体验2. 字体优化使用 next/font:import { Inter } from 'next/font/google';const inter = Inter({ subsets: ['latin'], display: 'swap', variable: '--font-inter',});export default function RootLayout({ children }) { return ( <html className={inter.variable}> <body>{children}</body> </html> );}优化策略:只加载需要的字符集使用 display: swap 避免布局偏移使用 CSS 变量应用字体3. CSS 优化CSS-in-JS 优化:// 使用 styled-componentsimport styled from 'styled-components';const Button = styled.button` background: ${props => props.theme.primary}; color: white; padding: 10px 20px;`;CSS Modules:自动提取和压缩 CSS避免样式冲突更好的缓存策略渲染策略优化1. 静态生成(SSG)使用 getStaticProps:export async function getStaticProps() { const posts = await getPosts(); return { props: { posts }, revalidate: 3600, // ISR:每小时重新生成 };}优势:预渲染 HTMLCDN 缓存极快的加载速度更好的 SEO2. 增量静态再生成(ISR)按需重新验证:export async function getStaticPaths() { const posts = await getPosts(); return { paths: posts.map(post => ({ params: { id: post.id } })), fallback: 'blocking' };}export async function getStaticProps({ params }) { const post = await getPost(params.id); return { props: { post }, revalidate: 60, // 60 秒后可以重新生成 };}// API 路由用于手动重新验证// pages/api/revalidate.jsexport default async function handler(req, res) { const { id } = req.query; await res.revalidate(`/posts/${id}`); res.json({ revalidated: true });}3. 服务端渲染(SSR)选择性使用 SSR:// 只对需要实时数据的页面使用 SSRexport async function getServerSideProps() { const data = await fetchRealTimeData(); return { props: { data } };}优化策略:只在必要时使用 SSR缓存 API 响应使用 Streaming 减少首屏时间缓存策略1. CDN 缓存配置缓存头:// vercel.json{ "headers": [ { "source": "/static/:path*", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ]}缓存策略:静态资源:长期缓存API 响应:短期缓存HTML:根据内容变化2. 数据缓存使用 Vercel KV:import { kv } from '@vercel/kv';export async function getPosts() { const cached = await kv.get('posts'); if (cached) { return JSON.parse(cached); } const posts = await fetchPosts(); await kv.set('posts', JSON.stringify(posts), { ex: 3600 }); return posts;}3. 客户端缓存使用 SWR:import useSWR from 'swr';const fetcher = url => fetch(url).then(r => r.json());function Posts() { const { data, error } = useSWR('/api/posts', fetcher, { revalidateOnFocus: false, revalidateOnReconnect: false, dedupingInterval: 60000 }); if (error) return <div>Error</div>; if (!data) return <div>Loading...</div>; return <PostsList posts={data} />;}网络优化1. HTTP/2 和 HTTP/3Vercel 自动支持 HTTP/2 和 HTTP/3,无需额外配置。优势:多路复用头部压缩服务器推送更快的连接建立2. 预加载和预连接预加载关键资源:export default function Document() { return ( <Html> <Head> <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossOrigin="" /> <link rel="preconnect" href="https://api.example.com" /> </Head> <body /> </Html> );}3. 减少请求合并请求:使用 GraphQL 减少请求次数批量 API 调用使用请求合并中间件性能监控1. Vercel Analytics集成 Analytics:import { Analytics } from '@vercel/analytics/react';export default function RootLayout({ children }) { return ( <html> <body> {children} <Analytics /> </body> </html> );}监控指标:Web Vitals(LCP、FID、CLS)页面加载时间用户行为分析2. 自定义监控性能追踪:export async function getServerSideProps() { const start = Date.now(); const data = await fetchData(); const duration = Date.now() - start; // 发送到监控服务 await logMetric('data_fetch_duration', duration); return { props: { data } };}Edge Runtime 优化1. 使用 Edge Runtime配置 Edge Runtime:export const runtime = 'edge';export default function handler(request) { return new Response('Hello from Edge!');}优势:更快的冷启动更低的延迟全球边缘执行更好的性能2. Edge Middleware使用 Middleware:import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server';export function middleware(request: NextRequest) { // 在边缘执行,减少延迟 const response = NextResponse.next(); // 添加自定义头 response.headers.set('X-Custom-Header', 'value'); return response;}export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],};数据库优化1. 连接池重用数据库连接:import { PrismaClient } from '@prisma/client';const globalForPrisma = global as unknown as { prisma: PrismaClient };export const prisma = globalForPrisma.prisma || new PrismaClient();if (process.env.NODE_ENV !== 'production') { globalForPrisma.prisma = prisma;}2. 查询优化优化数据库查询:// 只选择需要的字段const users = await prisma.user.findMany({ select: { id: true, name: true, email: true }});// 使用索引const user = await prisma.user.findUnique({ where: { email: userEmail }});// 分页const users = await prisma.user.findMany({ skip: 0, take: 10});构建配置优化1. Next.js 配置优化 next.config.js:/** @type {import('next').NextConfig} */const nextConfig = { // 启用压缩 compress: true, // 优化图片 images: { formats: ['image/avif', 'image/webp'], deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], }, // 实验性功能 experimental: { optimizeCss: true, }, // 生产环境优化 productionBrowserSourceMaps: false, // 减少输出大小 swcMinify: true,};module.exports = nextConfig;2. Webpack 配置自定义 webpack 配置:module.exports = { webpack: (config, { isServer }) => { // 优化打包 config.optimization = { ...config.optimization, splitChunks: { chunks: 'all', cacheGroups: { default: false, vendors: false, vendor: { name: 'vendor', chunks: 'all', test: /node_modules/, priority: 20 }, common: { name: 'common', minChunks: 2, chunks: 'all', priority: 10, reuseExistingChunk: true, enforce: true } } } }; return config; }};部署优化1. 区域选择选择最近的区域:{ "regions": ["iad1"]}可用区域:iad1:美国东部hkg1:香港sin1:新加坡等等2. 构建缓存利用 Vercel 缓存:Vercel 自动缓存 node_modules缓存构建产物使用增量构建最佳实践总结1. 监控和分析使用 Vercel Analytics 监控性能定期检查 Web Vitals分析用户行为数据识别性能瓶颈2. 持续优化定期审查依赖优化图片和字体改进缓存策略测试不同渲染策略3. 性能预算设置性能预算监控包大小限制资源加载时间定期进行性能审计4. 测试和验证使用 Lighthouse 进行性能测试在不同网络条件下测试监控真实用户数据(RUM)进行 A/B 测试常见性能问题及解决方案1. 首屏加载慢解决方案:使用 SSG 或 ISR优化关键渲染路径预加载关键资源使用 Skeleton 加载状态2. 交互延迟高解决方案:减少主线程工作使用 Web Workers优化 JavaScript 执行使用防抖和节流3. 内存占用高解决方案:优化数据加载使用虚拟列表及时清理不再使用的资源避免内存泄漏通过以上优化策略,可以显著提升 Vercel 应用的性能,提供更好的用户体验。记住,性能优化是一个持续的过程,需要不断地监控、分析和改进。
阅读 0·2月21日 16:28

如何在 Vercel 上实现 CI/CD 流程?

如何在 Vercel 上实现 CI/CD 流程?Vercel 提供了强大的 CI/CD(持续集成/持续部署)功能,使开发者能够自动化构建、测试和部署流程。以下是实现 Vercel CI/CD 流程的详细指南。Vercel CI/CD 基础1. 自动化部署Vercel 通过 Git 集成实现自动化部署:支持的 Git 提供商:GitHubGitLabBitbucket部署触发条件:推送代码到仓库创建 Pull Request合并 Pull Request 到主分支推送标签2. 部署环境Vercel 支持三种部署环境:Production(生产环境):部署到主域名使用生产环境变量URL: https://your-project.vercel.appPreview(预览环境):为每个分支或 PR 生成唯一 URL使用预览环境变量URL: https://your-project-branch.vercel.appDevelopment(开发环境):本地开发使用使用开发环境变量通过 Vercel CLI 访问配置 CI/CD 流程1. Git 集成设置连接 Git 仓库:在 Vercel Dashboard 创建新项目选择 "Import Git Repository"授权 Vercel 访问你的 Git 账户选择要部署的仓库配置构建设置配置构建设置:// vercel.json{ "version": 2, "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install", "framework": null}2. 环境变量配置在 Dashboard 中配置:进入项目设置选择 "Environment Variables"添加环境变量选择适用的环境(Production、Preview、Development)通过 CLI 配置:# 添加生产环境变量vercel env add API_KEY production# 添加预览环境变量vercel env add API_KEY preview# 添加开发环境变量vercel env add API_KEY development在代码中使用:const apiKey = process.env.API_KEY;测试集成1. 单元测试配置测试脚本:// package.json{ "scripts": { "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage" }}在 CI 中运行测试:// vercel.json{ "buildCommand": "npm run build", "installCommand": "npm install", "scripts": { "postinstall": "npm run test" }}2. 集成测试使用 Playwright:npm install -D @playwright/test// tests/example.spec.jsimport { test, expect } from '@playwright/test';test('homepage has title', async ({ page }) => { await page.goto('https://your-project.vercel.app'); await expect(page).toHaveTitle(/Your Project/);});配置测试命令:{ "scripts": { "test:e2e": "playwright test" }}3. Linting 和代码质量检查配置 ESLint:npm install -D eslint// .eslintrc.jsmodule.exports = { extends: ['next/core-web-vitals', 'prettier'], rules: { 'no-console': 'warn', },};配置 Prettier:npm install -D prettier// .prettierrc{ "semi": true, "singleQuote": true, "tabWidth": 2}在 CI 中运行:{ "scripts": { "lint": "eslint .", "format": "prettier --write .", "format:check": "prettier --check ." }}高级 CI/CD 配置1. 自定义构建钩子使用 GitHub Actions:# .github/workflows/ci.ymlname: CIon: push: branches: [main, develop] pull_request: branches: [main]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - run: npm ci - run: npm test - run: npm run lint2. 部署前检查使用 Vercel CLI:# 构建检查vercel build --yes# 部署到预览环境vercel --yes# 部署到生产环境vercel --prod --yes3. 条件部署基于分支的部署:// vercel.json{ "git": { "deploymentEnabled": { "main": true, "develop": true, "feature/*": false } }}部署策略1. 蓝绿部署Vercel 支持蓝绿部署策略:配置蓝绿部署:// vercel.json{ "alias": ["your-project.vercel.app"], "domains": ["your-project.com"]}工作流程:部署新版本到预览环境测试新版本切换流量到新版本如有问题,快速回滚2. 金丝雀发布使用 Vercel 的流量分割:// vercel.json{ "alias": ["your-project.vercel.app"], "domains": ["your-project.com"], "routes": [ { "src": "/(.*)", "dest": "/$1" } ]}实现金丝雀发布:部署新版本逐步增加流量到新版本监控指标完全切换或回滚3. 滚动更新Vercel 自动处理滚动更新:零停机部署逐步替换旧版本自动健康检查失败自动回滚监控和通知1. 部署状态监控查看部署状态:# 查看最新部署vercel ls# 查看部署详情vercel inspect <deployment-url># 查看部署日志vercel logs <deployment-url>2. 通知配置Slack 通知:在 Vercel Dashboard 中配置 Slack 集成选择要通知的事件(部署成功、失败等)配置通知频道Email 通知:在项目设置中配置邮件通知选择要通知的事件添加收件人邮箱Webhook 通知:// vercel.json{ "webhooks": [ { "url": "https://your-webhook-endpoint.com/deploy", "events": ["deployment.success", "deployment.error"] } ]}性能优化1. 构建缓存Vercel 自动缓存以下内容:node_modules构建产物依赖下载优化缓存:// vercel.json{ "build": { "env": { "CACHE_KEY": "custom-cache-key" } }}2. 增量构建配置增量构建:// next.config.jsmodule.exports = { experimental: { incrementalCacheHandlerPath: './cache-handler.js', },};3. 并行构建Vercel 支持并行构建:同时构建多个项目并行执行构建步骤提高构建速度安全最佳实践1. 环境变量安全不要在代码中硬编码密钥:// ❌ 不好const apiKey = 'your-api-key-here';// ✅ 好const apiKey = process.env.API_KEY;使用环境变量:在 Dashboard 中配置使用 CLI 添加定期轮换密钥2. 访问控制配置团队权限:设置团队成员角色限制部署权限审计部署历史使用 SSO:启用单点登录集中管理访问提高安全性3. 依赖安全使用 Dependabot:# .github/dependabot.ymlversion: 2updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly"定期更新依赖:使用 npm audit 检查漏洞及时更新依赖使用 npm ci 安装依赖故障排除1. 构建失败常见原因:依赖版本冲突环境变量缺失测试失败内存不足解决方案:查看构建日志检查依赖版本验证环境变量增加内存限制2. 部署超时原因:构建时间过长网络问题资源限制解决方案:优化构建流程使用增量构建检查网络连接升级到付费计划3. 环境变量问题原因:变量未设置变量名称错误环境不匹配解决方案:检查环境变量配置验证变量名称确认环境设置重新部署项目最佳实践总结1. 分支策略Git Flow:main:生产环境develop:开发环境feature/*:功能分支hotfix/*:紧急修复Trunk-Based Development:所有开发在主分支使用功能标志频繁部署2. 测试策略测试金字塔:单元测试:快速、大量集成测试:中等数量E2E 测试:少量、关键路径测试覆盖率:设置覆盖率目标监控覆盖率变化定期审查测试质量3. 部署频率持续部署:自动部署通过测试的代码快速反馈小步快跑持续交付:自动构建和测试手动触发部署更好的控制4. 监控和告警关键指标:部署成功率构建时间测试通过率错误率告警配置:部署失败告警错误率告警性能下降告警通过以上配置和最佳实践,可以在 Vercel 上建立高效、可靠的 CI/CD 流程,提高开发效率和代码质量。
阅读 0·2月21日 16:28