Divisão de código
Por padrão, um bundler junta toda a sua app em um único arquivo JavaScript, e cada import no início de um arquivo contribui para ele. Esse bundle único cresce com a app, e o navegador precisa baixar tudo antes de qualquer coisa aparecer na tela.
Em uma máquina rápida com Wi-Fi forte você pode nunca notar. Em um celular com conexão fraca, uma biblioteca pesada consegue bloquear todo o carregamento inicial, inclusive recursos que não precisam nada dessa biblioteca. O curso demonstra isso com uma lista de produtos que depende de um grande pacote de dados fake: com tudo em um bundle, até o contador na mesma página fica em branco por mais de vinte segundos em uma conexão limitada.
Divisão de código é a solução. Ela quebra o bundle em pedaços, chamados chunks, e baixa um chunk apenas se e quando o usuário realmente precisa dele. Recursos que o usuário nunca usa nunca são baixados.
Dynamic import()
A primitiva por trás disso é JavaScript puro. Um import no topo do arquivo sempre é executado. Chamar import() como uma função carrega um módulo sob demanda, de qualquer lugar do seu código, e retorna uma promise que se resolve com o módulo.
// sempre carregado, parte do bundle principal
import { formatPrice } from './format'
// carregado apenas quando esse handler executa
async function showChart() {
const { renderChart } = await import('./charts')
renderChart()
}Bundlers tratam cada chamada de import() como uma linha de corte: o módulo importado, mais as dependências que ele não compartilha com o bundle principal, recebem seu próprio chunk, buscado pela rede na primeira vez que a chamada é executada.
lazy e Suspense
React envolve essa primitiva em um par de ferramentas. lazy recebe uma função que retorna um import() dinâmico e te devolve um componente que você pode renderizar como qualquer outro. A promise do loader precisa se resolver para um módulo cujo export padrão é o componente; outros named exports ao lado dele são aceitáveis. Para um arquivo que você não controla e que exporta apenas componentes nomeados, adapte a promise assim: lazy(() => import('./charts').then(m => ({ default: m.RevenueChart }))).
Renderizar um componente lazy suspende: React pausa a renderização dessa parte da árvore até o chunk chegar. Sozinho isso é um erro, então você envolve o componente lazy em um Suspense boundary com uma prop fallback, alguma UI para mostrar enquanto o download está em progresso.
import { lazy, Suspense, useState } from 'react'
const ProductsList = lazy(() => import('./ProductsList'))
export default function App() {
const [showProducts, setShowProducts] = useState(false)
return (
<>
<Counter />
<button onClick={() => setShowProducts(prev => !prev)}>
Mostrar produtos
</button>
{showProducts && (
<Suspense fallback={<h2>Carregando...</h2>}>
<ProductsList />
</Suspense>
)}
</>
)
}Agora o bundle principal não contém mais ProductsList nem sua dependência pesada. O contador aparece imediatamente. Clicar no botão inicia o download do chunk, o fallback mostra enquanto carrega, e o componente renderiza quando chega. Após esse primeiro carregamento o módulo é cachado, então esconder e mostrar novamente é instantâneo.
Nota de versão
lazy e Suspense são named imports de react. Código mais antigo, incluindo as aulas do curso, escreve eles como React.lazy e React.Suspense; são as mesmas APIs.
Divida por rotas primeiro
Você poderia procurar por componentes individuais pesados, mas a linha de corte padrão na maioria das apps é a rota. Cada página é uma unidade natural: um visitante na página inicial não tem uso para o código do dashboard, então faça cada página um componente lazy e deixe o roteamento decidir quais chunks são baixados.
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const Home = lazy(() => import('./pages/Home'))
const Vans = lazy(() => import('./pages/Vans'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
export default function App() {
return (
<BrowserRouter>
<Suspense fallback={<h2>Carregando...</h2>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/vans" element={<Vans />} />
<Route path="/host" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}Um Suspense boundary ao redor das rotas cobre cada página. O carregamento inicial busca o bundle principal mais apenas o chunk para a URL atual; outras páginas são baixadas quando o usuário navega para elas. Essa amplitude tem um custo: navegar para uma página cujo chunk não chegou ainda substitui toda a área da rota pelo fallback, então mantenha o layout compartilhado como header e nav fora do boundary e eles ficam no lugar durante a troca.
Divisão tem um limite, porém. Cada chunk custa uma requisição de rede extra e um momento de UI de fallback, então dividir um componente minúsculo não economiza nada que valha esse preço. Deixe componentes pequenos e qualquer coisa acima da dobra que os usuários veem imediatamente no bundle principal. Divida o que é pesado, raramente usado, ou atrás de uma navegação.
Você envolve um import dinâmico em lazy, coloca o componente dentro de Suspense com um fallback como uma mensagem de carregamento, e React mostra o fallback enquanto o código baixa. O resto da sua app aparece imediatamente.
Próximo: Autenticação, onde sessions, tokens, e aplicação do lado do servidor se unem.

