前端性能交付前:核对体验指标和异常页面
功能测试通过后,仍要检查构建产物、关键交互、弱网行为和长时间运行的资源释放。本文给出交付前的检查顺序,帮助把风险前移到发布前。
并发量、时长和性能阈值应来自产品的容量目标。无法覆盖的设备或网络条件需要明确记录,并准备相应的降级或回滚方案。
交付前性能验收体系
一份生产级的交付前检查清单,必须涵盖网络传输、 Core Web Vitals 三项核心指标(LCP, FID/INP, CLS)、运行期显存/堆内存稳定性以及第三方 SDK 阻断治理。
flowchart TD A[准备交付的构建产物 Dist] --> B(第一关:包体积与 Chunk 依赖树审计) B -->|检查通过| C(第二关:Core Web Vitals 自动化指标核验) C -->|检查通过| D(第三关:长时间运行内存泄漏快照比对) D -->|检查通过| E(第四关:弱网与边缘离线降级卡点) B -- 包含未解包大库/重复打包 -- F[阻断上线 Block Release] C -- LCP > 2.5s 或 CLS > 0.1 -- F D -- Delta Heap > 5MB 无回收 -- F E -- 无 Network Timeout 降级 -- F E -->|全量通过| G[签发上线 Release 合格证]核心四大检查关卡:
- 构建产物关:是否存在重复打包的
lodash/moment?是否存在未剔除的sourcemap与测试 Mock 数据? - Core Web Vitals 指标关:LCP (最大内容渲染时间) 是否小于 2.5 秒?INP (交互延迟) 是否低于 200 毫秒?CLS (累积布局偏移) 是否低于 0.1?
- 内存泄漏关:连续点击与组件挂载/卸载 50 次后,堆内存 (JS Heap) 是否能恢复至初始基线?
- 网络降级关:在 Fast 3G 弱网或模拟 10% 丢包状态下,系统是否有可读的 Skeleton 屏与超时兜底?
自动化检查脚本代码
第一步:编写 Node.js 构建产物自动化包体积门禁脚本。
// scripts/verify_bundle_size.js const fs = require('fs'); const path = require('path'); const MAX_TOTAL_SIZE_MB = 3.5; // 限制总体积不超过 3.5MB const MAX_INITIAL_CHUNK_KB = 500; // 限制首屏 Initial Chunk 不超过 500KB function checkBundleLimits() { const distDir = path.join(__dirname, '../dist/assets'); if (!fs.existsSync(distDir)) { console.error('❌ 错误:dist 目录不存在,请先执行 npm run build'); process.exit(1); } const files = fs.readdirSync(distDir); let totalSize = 0; let hasViolation = false; console.log('🔍 开始执行交付前打包体积检查...'); files.forEach((file) => { const filePath = path.join(distDir, file); const stats = fs.statSync(filePath); const sizeKb = stats.size / 1024; totalSize += stats.size; // 检查首屏 JS Chunk 体积 if (file.endsWith('.js') && !file.includes('async-') && sizeKb > MAX_INITIAL_CHUNK_KB) { console.error(`❌ 体积超标警告: 初始 Chunk [${file}] 大小为 ${sizeKb.toFixed(2)} KB,超过限制 ${MAX_INITIAL_CHUNK_KB} KB`); hasViolation = true; } }); const totalMb = totalSize / (1024 * 1024); console.log(`📦 产物总体积: ${totalMb.toFixed(2)} MB`); if (totalMb > MAX_TOTAL_SIZE_MB) { console.error(`❌ 总体积超标: 当前总大小 ${totalMb.toFixed(2)} MB 超过限制 ${MAX_TOTAL_SIZE_MB} MB`); hasViolation = true; } if (hasViolation) { console.error('⛔ 交付前检查不通过!上线构建已强制阻断。'); process.exit(1); } else { console.log('✅ 构建产物体积检查通过!'); } } checkBundleLimits();第二步:编写自动化 Playwright 脚本,用于在 headless 浏览器中模拟 50 次页面组件反复挂载与卸载,比对堆内存泄漏。
// tests/memory_leak_check.spec.ts import { test, expect } from '@playwright/test'; test('交付前检查:长时组件挂载卸载无内存泄漏', async ({ page }) => { await page.goto('http://localhost:3000'); // 1. 获取初始 JS Heap 内存快照 const initialHeap = await page.evaluate(() => (performance as any).memory?.usedJSHeapSize || 0); console.log(`初始 JS 堆内存大小: ${(initialHeap / 1024 / 1024).toFixed(2)} MB`); // 2. 模拟用户高频反复切换 50 次模态框与复杂图表组件 for (let i = 0; i < 50; i++) { await page.click('#open-complex-dialog-btn'); await page.waitForSelector('.dialog-content'); await page.click('#close-dialog-btn'); await page.waitForTimeout(50); } // 3. 强制触发 Garbage Collection(需要启动参数 --js-flags="--expose-gc") await page.evaluate(() => { if ((window as any).gc) (window as any).gc(); }); // 4. 获取终态内存大小 const finalHeap = await page.evaluate(() => (performance as any).memory?.usedJSHeapSize || 0); const heapDiffMb = (finalHeap - initialHeap) / 1024 / 1024; console.log(`50 次循环后 JS 堆内存大小: ${(finalHeap / 1024 / 1024).toFixed(2)} MB, 净增: ${heapDiffMb.toFixed(2)} MB`); // 内存净增不得超过 3MB(允许少量 JIT 编译开销) expect(heapDiffMb).toBeLessThan(3.0); });终端审计与报告验证
上线前在终端中依次运行检查命令。
首先,执行 Vite/Webpack 依赖分析器:
# 生成打包产物分析图谱 npx vite-bundle-visualizer -o ./dist/stats.html命令行日志输出:
[Bundle Visualizer] Analyzing build outputs... Found duplicate module: lodash-es included in 3 chunks! Action: Re-routing to vendor-shared chunk via manualChunks config.其次,调用真实 Lighthouse CI 在 headless 模式下运行评分:
npx lhci collect --url="http://localhost:3000" && npx lhci assert输出 Core Web Vitals 最终结论:
✅ Performance Score: 96 / 100 ✅ Largest Contentful Paint (LCP): 1.4s (Target: <2.5s) ✅ Interaction to Next Paint (INP): 45ms (Target: <200ms) ✅ Cumulative Layout Shift (CLS): 0.02 (Target: <0.1) ✅ Bundle Size Check Passed. ✅ Memory Leak Check Passed. Status: READY FOR PRODUCTION.把交付前检查做成自动化断言,不仅消除了人为经验遗漏的不确定性,更给每一次生产发布盖上了坚固的技术合格章。