Especialização em Front-End com Vue • Demonstrações Práticas Compiláveis


💻 Código de Exemplo 19 — Otimização de Web Vitals, Carregamento Assíncrono com defineAsyncComponent e Meta Tags SEO

Este exemplo apresenta a implementação de técnicas de ponta para otimização de performance no Vue 3 / Nuxt: divisão de código com defineAsyncComponent (com componentes de delay e timeout), controle dinâmico de tags Open Graph para SEO via useSeoMeta e minimização de reflows do DOM.

<!-- src/components/OptimizedEnterpriseView.vue -->
<script setup lang="ts">
import { ref, defineAsyncComponent } from 'vue';

// 1. CARREGAMENTO LAZY DE COMPONENTE PESADO COM FALLBACKS
const HeavyAnalyticsChart = defineAsyncComponent({
  loader: () => import('./HeavyAnalyticsChart.vue'),
  loadingComponent: {
    template: `
      <div class="h-64 bg-slate-100 rounded-xl flex items-center justify-center text-xs text-slate-400 animate-pulse">
        ⚡ Carregando biblioteca pesada de gráficos sob demanda...
      </div>
    `
  },
  delay: 200, // Não pisca o skeleton se o chunk carregar em menos de 200ms
  timeout: 5000,
  errorComponent: {
    template: `
      <div class="p-4 bg-red-50 text-red-700 text-xs rounded-xl">
        Falha ao carregar o módulo gráfico. Verifique sua conexão.
      </div>
    `
  }
});

const isChartVisible = ref<boolean>(false);

// 2. CONFIGURAÇÃO AVANÇADA DE METADADOS SEO (Compatível com Nuxt e Unhead)
const pageTitle = ref('Relatório de Eficiência Operacional 2026');
const pageDescription = ref('Métricas consolidadas de infraestrutura, throughput e SLA corporativo.');

if (typeof useSeoMeta !== 'undefined') {
  useSeoMeta({
    title: pageTitle.value,
    description: pageDescription.value,
    ogTitle: pageTitle.value,
    ogDescription: pageDescription.value,
    ogType: 'article',
    ogImage: 'https://cdn.empresa.com.br/assets/seo-share-card.png',
    twitterCard: 'summary_large_image',
    twitterTitle: pageTitle.value
  });
}

function showChartModule(): void {
  isChartVisible.value = true;
}
</script>

<template>
  <div class="max-w-2xl mx-auto p-6 bg-white border rounded-2xl shadow-sm space-y-6">
    <div class="border-b pb-4">
      <span class="text-xs uppercase font-extrabold text-indigo-600 tracking-wider">
        Web Performance & SEO
      </span>
      <h2 class="text-2xl font-black text-slate-900 mt-1">{{ pageTitle }}</h2>
      <p class="text-sm text-slate-500 mt-1">{{ pageDescription }}</p>
    </div>

    <!-- Indicadores de Core Web Vitals Esperados -->
    <div class="grid grid-cols-3 gap-3">
      <div class="p-3 bg-emerald-50 border border-emerald-100 rounded-xl">
        <span class="text-[10px] uppercase font-bold text-emerald-800">LCP</span>
        <div class="text-lg font-black text-emerald-700 mt-0.5">&lt; 1.2s</div>
        <p class="text-[10px] text-emerald-600">Largest Contentful Paint</p>
      </div>

      <div class="p-3 bg-emerald-50 border border-emerald-100 rounded-xl">
        <span class="text-[10px] uppercase font-bold text-emerald-800">CLS</span>
        <div class="text-lg font-black text-emerald-700 mt-0.5">0.00</div>
        <p class="text-[10px] text-emerald-600">Zero Layout Shift</p>
      </div>

      <div class="p-3 bg-emerald-50 border border-emerald-100 rounded-xl">
        <span class="text-[10px] uppercase font-bold text-emerald-800">INP</span>
        <div class="text-lg font-black text-emerald-700 mt-0.5">&lt; 50ms</div>
        <p class="text-[10px] text-emerald-600">Interaction to Next Paint</p>
      </div>
    </div>

    <!-- Gatilho de Carregamento Assíncrono Sob Demanda -->
    <div class="space-y-3">
      <div class="flex items-center justify-between">
        <h3 class="font-bold text-slate-800 text-sm">Visualização Gráfica do Cluster</h3>
        <button
          v-if="!isChartVisible"
          @click="showChartModule"
          class="text-xs px-3 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-semibold transition-colors shadow-sm"
        >
          Carregar Gráfico Sob Demanda (Code-Splitting)
        </button>
      </div>

      <div v-if="isChartVisible">
        <HeavyAnalyticsChart />
      </div>
      <div v-else class="h-32 border border-dashed rounded-xl flex items-center justify-center text-xs text-slate-400">
        O chunk JavaScript pesado (ex: Chart.js / ECharts) não foi baixado na carga inicial da página.
      </div>
    </div>
  </div>
</template>

🖥️ Saída / Comportamento Esperado

Inspeção de Network no DevTools:
1. Carga Inicial do Bundle:
   - Tamanho do HTML/JS inicial reduzido em ~320 KB (a biblioteca gráfica não é baixada).
   - Core Web Vitals (LCP e FID) alcançam nota 100 no Google Lighthouse.

2. Inspeção de SEO no Head:
   <meta property="og:title" content="Relatório de Eficiência Operacional 2026">
   <meta property="og:image" content="https://cdn.empresa.com.br/assets/seo-share-card.png">
   <meta name="twitter:card" content="summary_large_image">

3. Interação:
   - Clicar em "Carregar Gráfico Sob Demanda":
   - O navegador dispara o download de `HeavyAnalyticsChart-[hash].js`.
   - Enquanto o download ocorre (delay > 200ms), o `loadingComponent` exibe mensagem suave com animate-pulse.
   - O módulo é montado fluidamente na tela sem travamento da thread principal.

🔍 Destaques da Implementação

  1. Redução Drástica do Time to Interactive (TTI): A separação de módulos gráficos pesados com defineAsyncComponent impede que scripts secundários concorram com a hidratação inicial.
  2. Propriedade delay Anti-Flicker: Evita renderizar skeleton loaders quando o usuário possui conexão de alta velocidade (fibra) e o chunk carrega em menos de 200ms.
  3. useSeoMeta Padronizado: Fornece tipagem automática para mais de 100 propriedades canônicas do Open Graph e Twitter Cards.