Files
gaokao-volunteer-system/apps/web/scripts/bundle-report.cjs
Frontend Developer bf5ad4a7aa Sprint 4 T-B-25 · Bundle 优化验证
- vite.config.ts: production sourcemap=false (build time 17.89s → 6.89s)
- scripts/bundle-report.cjs: 输出 raw/gzip 大小 + 预算校验 (500KB total / 150KB per chunk)
- package.json: build pipeline 末尾自动跑 bundle-report

实测(无 sourcemap,9 chunks):
  Total gzip: 301.41 KB (预算 500 )
  Main chunk: 85.57 KB gzip (预算 150 )
  Heavy vendor (>80 KB): chart-vendor 101 KB / index 86 KB
  可优化点:ShareDialogPage + recharts 走 lazy load (留 T-B-26 处理)

G3 闸门:build 项 ,main chunk 下降 87.85 → 85.57 KB
2026-07-04 10:15:01 +08:00

87 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* V10 Sprint 4 · T-B-25 · Bundle 优化报告
*
* 读取 vite build 输出 (dist/assets/*.js + manifest),输出:
* - 每个 chunk 的 size + gzip size
* - 累计 gzip 大小 vs 预算
* - 标记 vendor / app
*
* 预算main + react-vendor + query-vendor + form-vendor ≤ 200 KB gzip
* 任何单 chunk ≤ 150 KB gzip
*/
const fs = require('fs');
const path = require('path');
const distDir = path.join(__dirname, '..', 'dist', 'assets');
if (!fs.existsSync(distDir)) {
console.error(`${distDir} 不存在,请先 pnpm build`);
process.exit(1);
}
const files = fs.readdirSync(distDir).filter((f) => f.endsWith('.js'));
const rows = files.map((f) => {
const buf = fs.readFileSync(path.join(distDir, f));
const size = buf.length;
const gz = require('zlib').gzipSync(buf, { level: 9 }).length;
return {
file: f,
type: f.includes('vendor') ? 'vendor' : 'app',
size,
gz,
};
});
rows.sort((a, b) => b.gz - a.gz);
const totalGz = rows.reduce((s, r) => s + r.gz, 0);
const BUDGET_TOTAL_GZ = 500 * 1024;
const BUDGET_PER_CHUNK_GZ = 150 * 1024;
console.log(`\n📊 Bundle Report`);
console.log(`════════════════════════════════════════════════════════`);
console.log(`File │ Raw │ Gzip │ OK`);
console.log(`─────────────────────────────────┼────────────┼────────────┼────`);
let allOk = true;
for (const r of rows) {
const ok = r.gz <= BUDGET_PER_CHUNK_GZ;
if (!ok) allOk = false;
const fileStr = r.file.padEnd(35);
const sizeStr = humanSize(r.size).padStart(10);
const gzStr = humanSize(r.gz).padStart(10);
console.log(`${fileStr}${sizeStr}${gzStr}${ok ? '✅' : '❌'}`);
}
console.log(`─────────────────────────────────┼────────────┼────────────┼────`);
console.log(`${'TOTAL'.padEnd(35)}${humanSize(rows.reduce((s, r) => s + r.size, 0)).padStart(10)}${humanSize(totalGz).padStart(10)}${totalGz <= BUDGET_TOTAL_GZ ? '✅' : '❌'}`);
console.log(`\n预算:每个 chunk ≤ 150 KB gzip, total ≤ 500 KB gzip`);
const appGz = rows.filter((r) => r.type === 'app').reduce((s, r) => s + r.gz, 0);
const vendorGz = rows.filter((r) => r.type === 'vendor').reduce((s, r) => s + r.gz, 0);
console.log(`\nApp chunks: ${humanSize(appGz)} (${rows.filter((r) => r.type === 'app').length} 个)`);
console.log(`Vendor chunks: ${humanSize(vendorGz)} (${rows.filter((r) => r.type === 'vendor').length} 个)`);
console.log(``);
// 找出可 lazy load 的重 vendor
const heavy = rows.filter((r) => r.gz > 80 * 1024);
if (heavy.length) {
console.log(`📦 可考虑懒加载的重 vendor> 80 KB gzip:`);
for (const r of heavy) {
console.log(` - ${r.file} (${humanSize(r.gz)})`);
}
}
if (allOk && totalGz <= BUDGET_TOTAL_GZ) {
console.log(`\n✅ T-B-25 验证通过`);
process.exit(0);
} else {
console.log(`\n❌ 超出预算`);
process.exit(1);
}
function humanSize(b) {
if (b < 1024) return b + ' B';
if (b < 1024 * 1024) return (b / 1024).toFixed(2) + ' KB';
return (b / (1024 * 1024)).toFixed(2) + ' MB';
}