13

Module 13: Optimize Angular Micro Frontends with Shared Dependencies

Chapter 13 • Advanced

35 min

Optimize Angular Micro Frontends with Shared Dependencies

You will learn:

  • How to share Angular core libraries
  • How to avoid duplicate bundles
  • How to reduce initial load time
  • How to prevent version conflicts

This is critical for production systems.


The Problem: Duplicate Bundles

Without Sharing

code
Host App Bundle:     2.5 MB (includes Angular core)
Products App Bundle: 2.3 MB (includes Angular core)
Checkout App Bundle: 2.4 MB (includes Angular core)

Total: 7.2 MB (duplicate Angular core)

With Sharing

code
Host App Bundle:     2.5 MB (includes Angular core)
Products App Bundle: 0.8 MB (no Angular core)
Checkout App Bundle: 0.9 MB (no Angular core)

Total: 4.2 MB (Angular core loaded once)

Savings: 3 MB (42% reduction)


Shared Dependencies Strategy

What to Share

  • ✅ Angular core libraries (@angular/core, @angular/common, etc.)
  • ✅ RxJS (used everywhere)
  • ✅ Common utilities
  • ✅ Shared UI libraries

What NOT to Share

  • ❌ Domain-specific libraries
  • ❌ Large third-party libraries (unless needed)
  • ❌ App-specific code

Configuration for Performance

Optimal Shared Config

typescript.js
shared: {
  '@angular/core': {
    singleton: true,
    strictVersion: true,
    requiredVersion: '^15.0.0',
    eager: false  // Lazy load (default)
  },
  '@angular/common': {
    singleton: true,
    strictVersion: true,
    requiredVersion: '^15.0.0',
    eager: false
  },
  '@angular/router': {
    singleton: true,
    strictVersion: true,
    requiredVersion: '^15.0.0',
    eager: false
  },
  'rxjs': {
    singleton: true,
    strictVersion: true,
    requiredVersion: '^7.0.0',
    eager: false
  }
}

Bundle Size Analysis

Analyze Bundle Sizes

bash.js
# Build for production
npx ng build --configuration production

# Analyze bundles
npx webpack-bundle-analyzer dist/host-app/stats.json

What to Look For

  • ✅ Angular core loaded once
  • ✅ No duplicate libraries
  • ✅ Reasonable bundle sizes
  • ✅ Lazy loading working

Lazy Loading Strategy

Route-Based Lazy Loading

typescript.js
const routes: Routes = [
  {
    path: 'products',
    loadChildren: () => import('products/ProductsModule')
      .then(m => m.ProductsModule)  // Loaded on demand
  }
]

Benefits:

  • ✅ Initial bundle smaller
  • ✅ Load modules on demand
  • ✅ Better performance

Preloading Strategy

Preload All Modules

typescript.js
import { PreloadAllModules } from '@angular/router'

RouterModule.forRoot(routes, {
  preloadingStrategy: PreloadAllModules
})

Custom Preloading

typescript.js
import { PreloadingStrategy, Route } from '@angular/router'
import { Observable, of } from 'rxjs'

export class CustomPreloadStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable<any>): Observable<any> {
    if (route.data && route.data['preload']) {
      return load()
    }
    return of(null)
  }
}

Code Splitting

Automatic Code Splitting

Module Federation automatically splits:

  • ✅ Each remote app
  • ✅ Shared dependencies
  • ✅ Route-based chunks

Manual Code Splitting

typescript.js
// Lazy load a component
const LazyComponent = () => import('./lazy.component')
  .then(m => m.LazyComponent)

Caching Strategy

Cache remoteEntry.js

typescript.js
// Use versioned URLs
remotes: {
  products: 'products@https://products.example.com/v1.2.3/remoteEntry.js'
}

Cache Shared Dependencies

  • ✅ Use CDN for shared libraries
  • ✅ Version shared dependencies
  • ✅ Long cache headers

Performance Metrics

Key Metrics

  • Initial Load Time: Time to first paint
  • Time to Interactive: When app becomes usable
  • Bundle Size: Total JavaScript size
  • Network Requests: Number of HTTP requests

Target Metrics

  • ✅ Initial load < 3 seconds
  • ✅ Time to interactive < 5 seconds
  • ✅ Bundle size < 2 MB (gzipped)
  • ✅ Network requests < 20

Optimization Techniques

1. Tree Shaking

typescript.js
// Only import what you need
import { Component } from '@angular/core'  // ✅ Good
import * as Angular from '@angular/core'    // ❌ Bad

2. Lazy Loading

typescript.js
// Load on demand
loadChildren: () => import('./module')  // ✅ Good
import { Module } from './module'        // ❌ Bad

3. Code Splitting

typescript.js
// Split large features
const LargeFeature = () => import('./large-feature')  // ✅ Good

Monitoring Performance

Web Vitals

typescript.js
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'

getCLS(console.log)
getFID(console.log)
getFCP(console.log)
getLCP(console.log)
getTTFB(console.log)

Bundle Analysis

bash.js
# Install analyzer
npm install --save-dev webpack-bundle-analyzer

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

Common Performance Issues

Issue 1: Duplicate Dependencies

Problem: Same library loaded multiple times

Solution: Add to shared config

Issue 2: Large Initial Bundle

Problem: Everything loaded upfront

Solution: Use lazy loading

Issue 3: Slow Remote Loading

Problem: Network latency

Solution: Use CDN, enable caching


Best Practices

✅ Do

  • Share Angular core libraries
  • Use lazy loading
  • Monitor bundle sizes
  • Optimize shared dependencies
  • Use CDN for shared libraries

❌ Don't

  • Don't share everything
  • Don't load everything upfront
  • Don't ignore bundle sizes
  • Don't skip performance monitoring

Next Steps

Performance optimization complete!

Next module: Routing between micro frontends.