Aller au contenu principal

Chargement Différé

Lazy Load Components (Standalone)

import { Routes } from '@angular/router';

export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component')
.then(m => m.HomeComponent)
},
{
path: 'about',
loadComponent: () => import('./about/about.component')
.then(m => m.AboutComponent)
},
{
path: 'users/:id',
loadComponent: () => import('./user-detail/user-detail.component')
.then(m => m.UserDetailComponent)
}
];

Lazy Load Routes

// Main routes
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
},
{
path: 'shop',
loadChildren: () => import('./shop/shop.routes')
.then(m => m.SHOP_ROUTES)
}
];

// admin/admin.routes.ts
import { Routes } from '@angular/router';

export const ADMIN_ROUTES: Routes = [
{
path: '',
loadComponent: () => import('./admin-dashboard.component')
.then(m => m.AdminDashboardComponent)
},
{
path: 'users',
loadComponent: () => import('./admin-users.component')
.then(m => m.AdminUsersComponent)
}
];

Stratégies de Préchargement

Pas de Préchargement (Par défaut)

import { provideRouter } from '@angular/router';

bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes) // No preloading
]
});

PreloadAllModules

import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';

bootstrapApplication(AppComponent, {
providers: [
provideRouter(
routes,
withPreloading(PreloadAllModules) // Preload all lazy routes
)
]
});

Stratégie de Préchargement Personnalisée

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class CustomPreloadStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
// Preload if route has data.preload = true
if (route.data?.['preload']) {
console.log('Preloading:', route.path);
return load();
}
return of(null);
}
}

// Usage
export const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes'),
data: { preload: true } // Will preload
},
{
path: 'shop',
loadChildren: () => import('./shop/shop.routes')
// Won't preload
}
];

bootstrapApplication(AppComponent, {
providers: [
provideRouter(
routes,
withPreloading(CustomPreloadStrategy)
)
]
});

Chargement Différé avec Guards

export const routes: Routes = [
{
path: 'admin',
canActivate: [authGuard],
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
];

Analyse des Bundles

# Build and analyze bundles
ng build --stats-json
npx webpack-bundle-analyzer dist/your-app/stats.json

Chunks Nommés

// Webpack will create named chunks
{
path: 'admin',
loadChildren: () => import(
/* webpackChunkName: "admin" */
'./admin/admin.routes'
).then(m => m.ADMIN_ROUTES)
}

Chargement Différé des Services

// Service only loaded with lazy route
@Injectable()
export class AdminService {
// ...
}

// Fournir dans la route lazy
export const ADMIN_ROUTES: Routes = [
{
path: '',
component: AdminComponent,
providers: [AdminService] // Only loaded with this route
}
];

Tests Chargement Différé

describe('Lazy Loading', () => {
it('should lazy load admin module', async () => {
const router = TestBed.inject(Router);
const fixture = TestBed.createComponent(AppComponent);

await router.navigate(['/admin']);
fixture.detectChanges();

const compiled = fixture.nativeElement;
expect(compiled.querySelector('app-admin')).toBeTruthy();
});
});

Bonnes Pratiques

  • Charger en différé les routes de fonctionnalités (pas les fonctionnalités principales)
  • Utiliser PreloadAllModules pour une meilleure UX sur les connexions rapides
  • Créer des stratégies personnalisées pour le préchargement conditionnel
  • Charger en différé les routes admin/dashboard
  • Garder les fonctionnalités principales chargées immédiatement
  • Utiliser l'analyse des bundles pour optimiser
  • Précharger en fonction du comportement utilisateur ou de la priorité des routes
  • Regrouper les composants liés dans des modules de routes
  • Tester les routes lazy pour s'assurer qu'elles fonctionnent

Conseils de Performance

// ✅ Good: Separate features
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
}

// ❌ Bad: Too granular
{
path: 'user',
loadComponent: () => import('./user.component') // Too small
}

// ✅ Good: Group related components
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.routes')
// dashboard.routes contains multiple related components
}

Surveiller les Performances de Chargement

@Injectable({ providedIn: 'root' })
export class LoadTimePreloadStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data?.['preload']) {
const start = performance.now();

return load().pipe(
tap(() => {
const end = performance.now();
console.log(`Loaded ${route.path} in ${end - start}ms`);
})
);
}
return of(null);
}
}