2.6 — Performance e Tamanho do Bundle

Benchmarks reais e o impacto do compilador na performance.

Objetivos da Aula

  • Comparar tamanho de bundle entre Svelte e React
  • Entender métricas de performance (LCP, TTI, FCP)
  • Ver benchmarks de operações DOM
  • Analisar quando cada framework performa melhor

Tamanho do Bundle: A Grande Diferença

Hello World

Tamanho do Bundle — "Hello World"
React 18 + ReactDOM
42.2 KB (gzip)
Vue 3
22.8 KB (gzip)
Svelte 4
2.8 KB (gzip)
Svelte 5
4.5 KB (gzip)

Aplicação Real (TodoMVC)

Tamanho do Bundle — TodoMVC
React + ReactDOM + hooks
52 KB (gzip)
React + Redux Toolkit
68 KB
Vue 3 + Pinia
35 KB
Svelte (stores nativos)
7.5 KB

Por Que Essa Diferença?

// React precisa enviar:
// 1. react (~2.5KB gzip)
// 2. react-dom (~40KB gzip)
// 3. scheduler (~5KB)
// 4. Seu código
// = 47.5KB + seu código

// Svelte envia:
// 1. Helpers mínimos (~2KB)
// 2. Seu código (compilado eficientemente)
// = 2KB + seu código

Métricas de Performance

Core Web Vitals

Core Web Vitals

LCP (Largest Contentful Paint)

→ Quando o maior conteudo aparece

→ Meta: < 2.5s

FID (First Input Delay) / INP (Interaction to Next Paint)

→ Tempo ate responder a interacao

→ Meta: < 100ms (FID) / < 200ms (INP)

CLS (Cumulative Layout Shift)

→ Quanto a pagina "pula" durante carregamento

→ Meta: < 0.1

Outras Métricas Importantes

TTFB (Time to First Byte)
→ Tempo até primeiro byte do servidor
→ Afetado por: servidor, rede, CDN

FCP (First Contentful Paint)
→ Quando primeiro conteúdo aparece
→ Afetado por: tamanho do HTML, CSS crítico

TTI (Time to Interactive)
→ Quando a página fica interativa
→ Afetado por: tamanho do JS, hidratação

Benchmark: Operações DOM

js-framework-benchmark

Este benchmark testa operações comuns de UI:

Benchmark: Criar 1000 linhas
Svelte
145ms
Vue 3
165ms
React (hooks)
198ms
Angular
267ms
Vanilla JS
134ms (baseline)
Benchmark: Atualizar 1000 linhas (cada 10a)
Svelte
85ms
Vue 3
105ms
React (hooks)
142ms
Benchmark: Selecionar linha
Svelte
4.2ms
React (hooks)
12.8ms
Vue 3
9.4ms

Por Que Svelte é Mais Rápido?

// React: Cada atualização
// 1. Executa função do componente
// 2. Cria objetos Virtual DOM
// 3. Diff com árvore anterior
// 4. Aplica mudanças

// Svelte: Cada atualização
// 1. Atualiza DOM diretamente (código gerado sabe exatamente o que mudou)

// Exemplo: Atualizar um contador
// React: ~15 operações JavaScript
// Svelte: ~3 operações JavaScript

Hidratação: O Custo Oculto

O Que é Hidratação?

SSR + Hidratacao
  • Servidor renderiza HTML
    → HTML estatico chega ao navegador (FCP rapido!)
  • JavaScript carrega
    → Tamanho do bundle importa aqui
  • Hidratacao acontece
    → Framework "assume" o HTML estatico
    → Adiciona event listeners
    → Reconstroi estado
  • Pagina fica interativa (TTI)

Custo da Hidratação

Tempo de Hidratacao — App Medio (100 componentes)
React
~320ms

(precisa reconstruir Virtual DOM)

Vue
~220ms
Svelte
~120ms

(so adiciona listeners)


Memória

Consumo de RAM

Memoria — App com 1000 itens
React
15.2 MB

(Virtual DOM + Fiber nodes + closures)

Vue
11.5 MB

(Proxy objects + dependency tracking)

Svelte
7.8 MB

(DOM nodes + minimal state)


Quando React Pode Ser Mais Rápido

Concurrent Mode e Suspense

// React pode pausar renderização para manter UI responsiva
function HeavyList({ items }) {
  return (
    <Suspense fallback={<Skeleton />}>
      {items.map(item => (
        <ExpensiveItem key={item.id} data={item} />
      ))}
    </Suspense>
  )
}

// Transitions para updates de baixa prioridade
function Search() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [isPending, startTransition] = useTransition()

  function handleChange(e) {
    setQuery(e.target.value)
    startTransition(() => {
      setResults(searchItems(e.target.value))
    })
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending && <Spinner />}
      <Results items={results} />
    </>
  )
}

Quando Isso Importa

  • Apps com interações muito pesadas
  • Dashboards com muitos gráficos
  • Editores de texto ricos
  • Animações complexas durante updates

Svelte 5 está adicionando features similares com $effect.pre() e melhor scheduling.


Comparativo Prático

Cenário: Landing Page

Requisitos:
- Carregar rápido
- SEO importante
- Pouca interatividade

Vencedor: SVELTE
- Bundle muito menor
- TTI mais rápido
- FCP melhor

Cenário: Dashboard Admin

Requisitos:
- Muita interatividade
- Muitos dados
- Usuário já autenticado (menos foco em TTI inicial)

Ambos funcionam bem, mas:
- Svelte: Updates mais rápidos, menor memória
- React: Ecossistema maior de componentes prontos

Cenário: E-commerce

Requisitos:
- Performance crítica (conversão!)
- SEO essencial
- Carrinho interativo

Vencedor: SVELTE (ou qualquer um com SSR bem feito)
- Bundle menor = menos abandono em mobile
- TTI menor = mais conversão

Medindo na Prática

Lighthouse

# Install
npm install -g lighthouse

# Run
lighthouse https://seu-site.com --view

Web Vitals no Código

// Com a biblioteca web-vitals
import { getCLS, getFID, getLCP, getFCP, getTTFB } from 'web-vitals'

getCLS(console.log)   // Cumulative Layout Shift
getFID(console.log)   // First Input Delay
getLCP(console.log)   // Largest Contentful Paint
getFCP(console.log)   // First Contentful Paint
getTTFB(console.log)  // Time to First Byte

Bundle Analyzer

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'

export default {
  plugins: [
    visualizer({
      open: true,
      gzipSize: true
    })
  ]
}

✅ Desafio da Aula

Objetivo

Medir e comparar a performance de um componente em desenvolvimento.

Instruções

  1. Crie um componente que renderiza 500 itens
  2. Adicione um botão que atualiza todos os itens
  3. Meça o tempo de renderização inicial e de atualização
  4. Use performance.now() para medir

Componente Base

<script>
  let items = Array.from({ length: 500 }, (_, i) => ({
    id: i,
    value: Math.random()
  }))

  function updateAll() {
    const start = performance.now()
    items = items.map(item => ({
      ...item,
      value: Math.random()
    }))
    // Como medir o tempo de renderização?
  }
</script>

Spec de Verificação

  • 500 itens são renderizados
  • Botão atualiza todos os itens
  • Tempo de atualização é exibido na tela
  • Tempo é < 50ms (se não, algo está errado!)

Solução

🔍 Clique para ver a solução
<script>
  import { tick } from 'svelte'

  let items = Array.from({ length: 500 }, (_, i) => ({
    id: i,
    value: Math.random()
  }))

  let renderTime = 0
  let updateCount = 0

  async function updateAll() {
    const start = performance.now()

    items = items.map(item => ({
      ...item,
      value: Math.random()
    }))

    // tick() espera o Svelte completar a atualização do DOM
    await tick()

    renderTime = (performance.now() - start).toFixed(2)
    updateCount++
  }

  // Medir renderização inicial
  import { onMount } from 'svelte'
  let initialRender = 0

  onMount(async () => {
    const start = performance.now()
    await tick()
    initialRender = (performance.now() - start).toFixed(2)
  })
</script>

<div class="stats">
  <p>Renderização inicial: {initialRender}ms</p>
  <p>Última atualização: {renderTime}ms</p>
  <p>Updates: {updateCount}</p>
  <button on:click={updateAll}>Atualizar Todos</button>
</div>

<div class="grid">
  {#each items as item (item.id)}
    <div class="item">
      {item.value.toFixed(4)}
    </div>
  {/each}
</div>

<style>
  .stats {
    position: sticky;
    top: 0;
    background: white;
    padding: 1rem;
    border-bottom: 1px solid #ccc;
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
    gap: 4px;
    padding: 1rem;
  }

  .item {
    padding: 8px;
    background: #f0f0f0;
    text-align: center;
    font-size: 0.75rem;
  }
</style>

Próxima aula: 2.7 — Ecossistema e Mercado de Trabalho