💻 Exemplos: Capítulo 19: Otimização de Performance e Deferrable Views (@defer)
Especialização em Front-End com Angular • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 19 — Code-Splitting Granular com Deferrable Views (@defer)
Este exemplo apresenta a utilização dos blocos declarativos de Deferrable Views (@defer) do Angular 17/18/19, demonstrando como adiar o download e a renderização de componentes pesados através de gatilhos como on viewport, on interaction, prefetch on idle, com blocos de @placeholder, @loading e @error.
1. Componente Pesado a ser Adiado: src/app/components/heavy-analytics-chart.component.ts
// src/app/components/heavy-analytics-chart.component.ts
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-heavy-analytics-chart',
standalone: true,
imports: [CommonModule],
template: `
<div class="p-6 bg-slate-900 text-white rounded-2xl border border-slate-800 space-y-4 shadow-xl">
<div class="flex justify-between items-center border-b border-slate-800 pb-3">
<h3 class="font-bold text-sm text-emerald-400">📊 Gráfico Analítico de Throughput (ECharts Mock)</h3>
<span class="text-[10px] bg-slate-800 px-2 py-0.5 rounded text-slate-300 font-mono">
Chunk: heavy-analytics-chart.js (Baixado sob demanda)
</span>
</div>
<!-- Simulação de Grade Gráfica SVG -->
<div class="h-32 flex items-end gap-2 pt-4 px-2">
@for (height of barHeights(); track $index) {
<div
class="flex-1 bg-gradient-to-t from-red-600 to-indigo-500 rounded-t transition-all duration-500 hover:opacity-80"
[style.height.%]="height"
></div>
}
</div>
<div class="flex justify-between text-[11px] text-slate-400 font-mono pt-2 border-t border-slate-800">
<span>Throughput Médio: 48.200 req/s</span>
<span>Taxa de Erro: 0.02%</span>
</div>
</div>
`
})
export class HeavyAnalyticsChartComponent {
barHeights = signal<number[]>([35, 60, 45, 80, 95, 70, 85, 40, 65, 90]);
}
2. Componente com Bloco @defer: src/app/components/performance-dashboard.component.ts
// src/app/components/performance-dashboard.component.ts
import { Component, signal } from '@angular/core';
import { HeavyAnalyticsChartComponent } from './heavy-analytics-chart.component';
@Component({
selector: 'app-performance-dashboard',
standalone: true,
imports: [HeavyAnalyticsChartComponent],
template: `
<div class="max-w-2xl mx-auto p-6 bg-white border border-slate-200 rounded-2xl shadow-sm space-y-6">
<div class="border-b pb-4">
<h2 class="text-xl font-bold text-slate-800">⚡ Otimização com Deferrable Views (@defer)</h2>
<p class="text-xs text-slate-500">Divisão de bundle nativa sem necessidade de rotas separadas</p>
</div>
<p class="text-xs text-slate-600 leading-relaxed">
O bloco abaixo contém um componente pesado. O compilador do Angular dividiu este componente em um arquivo JavaScript
independente. O chunk só será baixado quando o usuário clicar no gatilho ou rolar a página até ele.
</p>
<!-- BOTÃO GATILHO PARA CARREGAMENTO MANUAL -->
<button
#triggerBtn
type="button"
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-xs font-bold transition-colors shadow-sm"
>
Carregar Módulo Gráfico (Gatilho on interaction)
</button>
<!-- BLOCO @DEFER MODERNO COM 4 ESTADOS -->
@defer (on interaction(triggerBtn); prefetch on idle) {
<!-- Componente pesado instanciado sob demanda -->
<app-heavy-analytics-chart />
} @placeholder (minimum 300ms) {
<!-- Renderizado inicialmente no HTML antes de qualquer interação -->
<div class="h-44 border-2 border-dashed border-slate-200 rounded-2xl flex flex-col items-center justify-center p-6 text-center space-y-1">
<span class="text-2xl">💤</span>
<p class="text-xs font-semibold text-slate-700">Módulo Gráfico Adiado (Placeholder)</p>
<p class="text-[11px] text-slate-400">Clique no botão acima para carregar o chunk.</p>
</div>
} @loading (after 100ms; minimum 500ms) {
<!-- Exibido enquanto o arquivo JavaScript está baixando pela rede -->
<div class="h-44 bg-slate-50 border rounded-2xl flex items-center justify-center gap-2 text-xs text-slate-500 animate-pulse">
<span class="w-4 h-4 border-2 border-red-600 border-t-transparent rounded-full animate-spin"></span>
<span>Baixando pacote JavaScript sob demanda...</span>
</div>
} @error {
<!-- Exibido se o download do chunk falhar (ex: rede offline) -->
<div class="p-4 bg-red-50 border border-red-200 text-red-700 text-xs rounded-xl">
⚠️ Falha de rede ao baixar o módulo gráfico. Verifique sua conexão.
</div>
}
</div>
`
})
export class PerformanceDashboardComponent {}
🖥️ Saída / Comportamento Esperado
Inspeção de Rede (DevTools Network Tab) na Carga da Página:
O arquivo 'heavy-analytics-chart.component.js' NÃO é baixado no carregamento inicial da página!
A tela exibe o bloco @placeholder:
"💤 Módulo Gráfico Adiado (Placeholder)"
Em momento ocioso do navegador (prefetch on idle):
O browser realiza o download prévio do chunk em background sem travar a thread principal.
Ao Clicar no Botão "Carregar Módulo Gráfico":
O gatilho on interaction(triggerBtn) é satisfeito.
O bloco @loading exibe brevemente o spinner animado (mínimo de 500ms para evitar piscar).
O componente <app-heavy-analytics-chart> é montado exibindo o gráfico escuro com barras gradientes.
🔍 Destaques da Implementação
- Zero Configuração de Build: O compilador do Angular 18 isola automaticamente os imports dentro de
@deferem novos chunks sem alterar oangular.json. prefetch on idleEstratégico: Faz o pré-carregamento dos bytes em momentos de ociosidade do navegador, garantindo renderização instantânea no clique.- Parâmetros
aftereminimum: Evitam o efeito desagradável de tela piscando (layout shift e flickering) em conexões de altíssima velocidade.