Files
gh-ehssanatassi-angular-mar…/commands/analyze-bundle.md
2025-11-29 18:25:00 +08:00

8.5 KiB

Analyze Bundle Command

Analyze, visualize, and optimize Angular bundle sizes.

Usage

/angular-performance:analyze-bundle

What It Does

  1. Generate bundle statistics
  2. Visualize bundle composition
  3. Identify optimization opportunities
  4. Provide actionable recommendations

Step 1: Generate Bundle Stats

# Build with stats
ng build --configuration production --stats-json

# Output: dist/<project>/stats.json

Step 2: Analyze with Tools

Webpack Bundle Analyzer

npm install --save-dev webpack-bundle-analyzer

# Analyze
npx webpack-bundle-analyzer dist/<project>/stats.json

Source Map Explorer

npm install --save-dev source-map-explorer

# Build with source maps
ng build --configuration production --source-map

# Analyze
npx source-map-explorer dist/**/*.js

Optimization Strategies

1. Lazy Loading

// angular.json - before
const routes: Routes = [
  { path: 'dashboard', component: DashboardComponent },
  { path: 'users', component: UsersComponent },
  { path: 'reports', component: ReportsComponent }
];

// After - lazy load feature modules
const routes: Routes = [
  {
    path: 'dashboard',
    loadComponent: () => import('./dashboard/dashboard.component')
      .then(m => m.DashboardComponent)
  },
  {
    path: 'users',
    loadChildren: () => import('./users/users.routes')
      .then(m => m.USERS_ROUTES)
  },
  {
    path: 'reports',
    loadChildren: () => import('./reports/reports.routes')
      .then(m => m.REPORTS_ROUTES)
  }
];

Impact: Reduces initial bundle by 40-60%

2. Tree Shaking

// Before: Imports entire library
import * as _ from 'lodash';

// After: Import only what you need
import { debounce, throttle } from 'lodash-es';

// Or use native alternatives
const debounce = (fn, delay) => {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
};

3. Remove Unused Dependencies

# Find unused dependencies
npx depcheck

# Analyze package size before adding
npx bundlephobia <package-name>

4. Angular Material - Import Selectively

// Before: Import entire Material
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatTableModule } from '@angular/material/table';
// ... 20+ imports

// After: Only import what you use per component
@Component({
  standalone: true,
  imports: [MatButtonModule], // Only button for this component
  template: '<button mat-raised-button>Click</button>'
})

5. Replace Heavy Libraries

// Before: Moment.js (67KB gzipped)
import * as moment from 'moment';
const date = moment().format('YYYY-MM-DD');

// After: date-fns (6KB gzipped)
import { format } from 'date-fns';
const date = format(new Date(), 'yyyy-MM-dd');

// Or native Intl API (0KB - built-in)
const date = new Intl.DateTimeFormat('en-US').format(new Date());

6. Code Splitting with Dynamic Imports

// Before: Import at top level
import { ChartComponent } from './chart/chart.component';

export class DashboardComponent {
  showChart = false;
  chart = ChartComponent; // Always in bundle
}

// After: Dynamic import
export class DashboardComponent {
  showChart = false;
  chartComponent: any;
  
  async loadChart() {
    const { ChartComponent } = await import('./chart/chart.component');
    this.chartComponent = ChartComponent;
    this.showChart = true;
  }
}

7. Optimize Images

# Install image optimization tools
npm install --save-dev imagemin imagemin-webp imagemin-pngquant

# Use WebP format with fallbacks
<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Description">
</picture>

8. Enable Build Optimizations

// angular.json
{
  "configurations": {
    "production": {
      "optimization": {
        "scripts": true,
        "styles": {
          "minify": true,
          "inlineCritical": true
        },
        "fonts": true
      },
      "outputHashing": "all",
      "sourceMap": false,
      "namedChunks": false,
      "aot": true,
      "extractLicenses": true,
      "buildOptimizer": true,
      "budgets": [
        {
          "type": "initial",
          "maximumWarning": "500kb",
          "maximumError": "1mb"
        },
        {
          "type": "anyComponentStyle",
          "maximumWarning": "6kb",
          "maximumError": "10kb"
        }
      ]
    }
  }
}

9. Differential Loading

Angular automatically generates ES5 and ES2015+ bundles:

<!-- Modern browsers get smaller ES2015+ bundle -->
<script src="main-es2015.js" type="module"></script>

<!-- Legacy browsers get ES5 bundle -->
<script src="main-es5.js" nomodule></script>

10. Service Worker & Caching

# Add service worker
ng add @angular/pwa

# Configures ngsw-config.json for caching

Bundle Budget Enforcement

// angular.json - Set budgets
"budgets": [
  {
    "type": "initial",
    "maximumWarning": "500kb",
    "maximumError": "1mb"
  },
  {
    "type": "anyComponentStyle",
    "maximumWarning": "6kb",
    "maximumError": "10kb"
  }
]

Analysis Report Example

📦 Bundle Analysis Report

📊 Current Bundle Size:
┌─────────────────┬──────────┬──────────┐
│ Chunk           │ Size     │ Gzipped  │
├─────────────────┼──────────┼──────────┤
│ main.js         │ 1.2 MB   │ 380 KB   │
│ polyfills.js    │ 145 KB   │ 45 KB    │
│ runtime.js      │ 12 KB    │ 4 KB     │
│ styles.css      │ 85 KB    │ 15 KB    │
├─────────────────┼──────────┼──────────┤
│ Total           │ 1.44 MB  │ 444 KB   │
└─────────────────┴──────────┴──────────┘

⚠️ Issues Found:

1. 🔴 Lodash (287 KB) - Replace with lodash-es or native
2. 🟡 Moment.js (67 KB) - Replace with date-fns (6 KB)
3. 🟡 Feature modules not lazy loaded (420 KB in main)
4. 🟡 Angular Material fully imported (180 KB unused)

✅ Recommended Actions:

1. Lazy load feature modules → Save ~400 KB
   - Dashboard, Users, Reports modules

2. Replace heavy libraries:
   - lodash → lodash-es → Save 250 KB
   - moment.js → date-fns → Save 60 KB

3. Tree-shake Material imports → Save 150 KB
   - Import only used components

4. Enable build optimizations → Save 100 KB
   - Already in angular.json

💡 Potential Savings: ~860 KB (59% reduction)

📈 After Optimization:
┌─────────────────┬──────────┬──────────┐
│ main.js         │ 580 KB   │ 180 KB   │
│ dashboard.js    │ 120 KB   │ 35 KB    │ (lazy)
│ users.js        │ 95 KB    │ 28 KB    │ (lazy)
│ reports.js      │ 205 KB   │ 62 KB    │ (lazy)
├─────────────────┼──────────┼──────────┤
│ Total Initial   │ 580 KB   │ 180 KB   │ ⬇️ 60%
│ Total All       │ 1.0 MB   │ 305 KB   │ ⬇️ 31%
└─────────────────┴──────────┴──────────┘

Continuous Monitoring

GitHub Action for Bundle Size

name: Bundle Size Check

on: [pull_request]

jobs:
  check-size:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build -- --stats-json
      
      - name: Analyze bundle
        uses: github/webpack-bundle-analyzer@v1
        with:
          bundle-stats: 'dist/stats.json'
          
      - name: Check size limits
        run: |
          SIZE=$(stat -f%z "dist/main.*.js")
          if [ $SIZE -gt 500000 ]; then
            echo "Bundle too large: $SIZE bytes"
            exit 1
          fi

Quick Wins Checklist

  • Enable production mode optimizations
  • Lazy load all feature modules
  • Replace moment.js with date-fns
  • Use lodash-es instead of lodash
  • Remove unused dependencies
  • Optimize images (WebP, lazy loading)
  • Import Material components selectively
  • Enable differential loading
  • Set up bundle budgets
  • Add service worker for caching

Smaller bundles = Faster apps! 📦