| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- /**
- * Bundle 分析脚本
- *
- * 自动化 bundle 分析流程:
- * 1. 构建生产版本
- * 2. 生成分析报告
- * 3. 输出关键指标
- *
- * 使用: node scripts/analyze-bundle.js
- */
- import { execSync } from 'node:child_process';
- import fs from 'node:fs';
- import path from 'node:path';
- // ANSI 颜色代码
- const colors = {
- reset: '\x1b[0m',
- bright: '\x1b[1m',
- green: '\x1b[32m',
- yellow: '\x1b[33m',
- blue: '\x1b[34m',
- red: '\x1b[31m',
- };
- function log(message, color = 'reset') {
- console.log(`${colors[color]}${message}${colors.reset}`);
- }
- function formatBytes(bytes) {
- if (bytes < 1024) return bytes + ' B';
- if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
- return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
- }
- async function main() {
- log('\n🔍 Bundle 分析工具\n', 'bright');
- // 步骤 1: 清理旧的构建产物
- log('📦 清理旧的构建产物...', 'blue');
- try {
- if (fs.existsSync('dist')) {
- fs.rmSync('dist', { recursive: true, force: true });
- }
- log('✅ 清理完成\n', 'green');
- } catch (error) {
- log(`❌ 清理失败: ${error.message}\n`, 'red');
- }
- // 步骤 2: 构建生产版本
- log('🏗️ 构建生产版本...', 'blue');
- try {
- execSync('npm run build', {
- stdio: 'inherit',
- env: { ...process.env, ANALYZE: 'true' }
- });
- log('✅ 构建完成\n', 'green');
- } catch (error) {
- log('❌ 构建失败,请检查错误信息\n', 'red');
- process.exit(1);
- }
- // 步骤 3: 分析构建产物
- log('📊 分析构建产物...\n', 'blue');
-
- const distPath = path.join(process.cwd(), 'dist', 'assets');
-
- if (!fs.existsSync(distPath)) {
- log('❌ dist/assets 目录不存在\n', 'red');
- return;
- }
- const files = fs.readdirSync(distPath);
-
- // 分类统计
- const stats = {
- js: [],
- css: [],
- other: [],
- };
- files.forEach((file) => {
- const filePath = path.join(distPath, file);
- const stat = fs.statSync(filePath);
- const size = stat.size;
- const fileInfo = {
- name: file,
- size,
- sizeFormatted: formatBytes(size),
- };
- if (file.endsWith('.js')) {
- stats.js.push(fileInfo);
- } else if (file.endsWith('.css')) {
- stats.css.push(fileInfo);
- } else {
- stats.other.push(fileInfo);
- }
- });
- // 排序(按大小降序)
- stats.js.sort((a, b) => b.size - a.size);
- stats.css.sort((a, b) => b.size - a.size);
- // 输出 JS 文件统计
- log('JavaScript 文件:', 'bright');
- stats.js.forEach((file, index) => {
- const color = file.size > 600 * 1024 ? 'red' : file.size > 200 * 1024 ? 'yellow' : 'green';
- const icon = file.size > 600 * 1024 ? '⚠️ ' : ' ';
- log(`${icon}${index + 1}. ${file.name.padEnd(50)} ${file.sizeFormatted}`, color);
- });
- const totalJS = stats.js.reduce((sum, file) => sum + file.size, 0);
- log(`\n总计: ${formatBytes(totalJS)}\n`, 'bright');
- // 输出 CSS 文件统计
- log('CSS 文件:', 'bright');
- stats.css.forEach((file, index) => {
- log(` ${index + 1}. ${file.name.padEnd(50)} ${file.sizeFormatted}`, 'green');
- });
- const totalCSS = stats.css.reduce((sum, file) => sum + file.size, 0);
- log(`\n总计: ${formatBytes(totalCSS)}\n`, 'bright');
- // 总体统计
- const total = totalJS + totalCSS;
- log('📈 总体统计:', 'bright');
- log(` JavaScript: ${formatBytes(totalJS)} (${((totalJS / total) * 100).toFixed(1)}%)`, 'blue');
- log(` CSS: ${formatBytes(totalCSS)} (${((totalCSS / total) * 100).toFixed(1)}%)`, 'blue');
- log(` 总计: ${formatBytes(total)}\n`, 'bright');
- // 警告信息
- const largeFiles = stats.js.filter(f => f.size > 600 * 1024);
- if (largeFiles.length > 0) {
- log('⚠️ 警告: 以下文件超过 600 KB,建议优化:', 'yellow');
- largeFiles.forEach((file) => {
- log(` - ${file.name}: ${file.sizeFormatted}`, 'yellow');
- });
- log('');
- }
- // 优化建议
- log('💡 优化建议:', 'bright');
-
- if (largeFiles.length > 0) {
- log(' 1. 检查是否可以进一步拆分大型 chunk', 'blue');
- log(' 2. 确认是否启用了 tree-shaking', 'blue');
- log(' 3. 考虑使用动态导入延迟加载', 'blue');
- } else {
- log(' ✅ Bundle 大小在合理范围内', 'green');
- }
- // 检查是否生成了分析报告
- const statsFile = path.join(process.cwd(), 'dist', 'stats.html');
- if (fs.existsSync(statsFile)) {
- log('\n📊 详细分析报告已生成: dist/stats.html', 'green');
- log(' 在浏览器中打开此文件查看可视化分析\n', 'blue');
- } else {
- log('\n⚠️ 未生成详细分析报告', 'yellow');
- log(' 请确保已安装 rollup-plugin-visualizer:', 'yellow');
- log(' npm install --save-dev rollup-plugin-visualizer\n', 'yellow');
- }
- }
- // 运行脚本
- main().catch((error) => {
- console.error('Error:', error);
- process.exit(1);
- });
|