Volver al blog
Angular Performance Lazy Loading Core Web Vitals
Imagen de portada del artículo: Optimización Extrema con Deferrable Views (@defer)

Optimización Extrema con Deferrable Views (@defer)

12 min
German

El Lazy Loading tradicionalmente se aplicaba a nivel de rutas. Si entrabas a una ruta, cargabas todo el módulo. ¿Pero qué pasa si tienes un componente muy pesado (como un gráfico complejo o un mapa) que está visible solo si el usuario hace scroll hacia abajo?

Aquí entra @defer. Esta sintaxis permite diferir la carga de partes específicas de tu plantilla de manera declarativa.

Sintaxis Básica y Bloques

El bloque @defer maneja varios estados automáticos:

html
@defer (on viewport) {
  <heavy-chart-component />
} @loading (minimum 1s) {
  <p>Cargando gráfico...</p>
} @placeholder {
  <img src="chart-placeholder.png" alt="Placeholder" />
} @error {
  <p>Hubo un error al cargar el componente.</p>
}

Desglose de los bloques:

  1. @defer: El contenido principal que se cargará perezosamente (el chunk de JS se separa automáticamente).
  2. @placeholder: Lo que se muestra antes de que comience la carga. Es instantáneo.
  3. @loading: Lo que se muestra mientras se descarga el código JS del componente.
  4. @error: Si falla la red o la carga del script.

Triggers (Disparadores)

La magia real está en cuándo se carga el contenido. Angular ofrece disparadores potentes:

Ejemplo Práctico: Dashboard con widgets pesados

Imagina un dashboard con un mapa, gráficos y una tabla de datos. Sin @defer, todo se carga al entrar:

html
<!-- Sin @defer: Todo se carga de golpe (bundle grande) -->
<app-header />
<app-kpi-cards />
<app-interactive-map />     <!-- 200KB de Leaflet -->
<app-chart-dashboard />     <!-- 150KB de Chart.js -->
<app-data-table />          <!-- 100KB de AG Grid -->

Con @defer, cada widget se carga solo cuando es necesario:

html
<app-header />
<app-kpi-cards />

@defer (on viewport) {
  <app-interactive-map />
} @placeholder {
  <div class="h-96 bg-gray-100 animate-pulse rounded-xl"></div>
}

@defer (on viewport) {
  <app-chart-dashboard />
} @loading (minimum 500ms) {
  <div class="flex items-center gap-2">
    <spinner /> Cargando gráficos...
  </div>
}

@defer (on interaction) {
  <app-data-table />
} @placeholder {
  <button class="btn">Haz clic para cargar la tabla completa</button>
}

Ejemplo: Comentarios de un Blog

No necesitamos cargar el componente de comentarios hasta que el usuario llegue al final del post.

html
@defer (on viewport) {
  <app-comments-section [postId]="id" />
} @placeholder {
  <div class="skeleton-comments">Cargar comentarios...</div>
}

Prefetching: Anticipar la carga

Puedes combinar @defer con prefetch para descargar el código antes de que se necesite:

html
@defer (on interaction; prefetch on hover) {
  <app-heavy-editor />
} @placeholder {
  <button>Abrir editor</button>
}

Cuando el usuario pasa el ratón por el botón, Angular empieza a descargar el JS en background. Cuando hace clic, el componente ya está listo.

Impacto en Core Web Vitals

Usar @defer correctamente mejora drásticamente:

En aplicaciones con muchos componentes pesados, hemos visto reducciones del 30-50% en el bundle inicial.