Guia de Desenvolvimento Frontend
Visão Geral
Este guia cobre o desenvolvimento frontend moderno com React, Vue, Angular e Svelte, incluindo arquitetura de componentes, otimização de desempenho, acessibilidade e testes. Use-o ao construir aplicações web, bibliotecas de componentes ou otimizar o desempenho frontend.
Regras de Decisão sobre Framework e Layout
- Ao escolher um framework, alinhe-o com a experiência da equipe e as restrições do projeto; por padrão, use React para ecossistema amplo, Vue para aprimoramento progressivo em páginas existentes, Svelte para aplicativos críticos em tamanho de bundle, e Angular quando o projeto exigir um framework completo e opinativo com DI e roteamento integrados.
- Ao implementar um design, use CSS Grid para layouts bidimensionais de página e Flexbox para alinhamento unidimensional de componentes; evite posicionamento absoluto para fins de layout, pois ele quebra o reflow responsivo.
- Ao construir uma biblioteca de componentes, exporte cada componente como uma exportação nomeada com uma interface TypeScript de props, uma história do Storybook e um teste unitário — componentes sem todos os três não são mesclados.
- Ao integrar com APIs de backend, centralize a lógica de fetch em uma camada de cliente de API tipada (por exemplo, um único módulo
api.tsusandofetchouaxioscom interceptadores) para que cabeçalhos de autenticação, transformações de erro e tentativas sejam tratados em um só lugar.
Regras de Decisão sobre Desempenho
- Quando o Maior Conteúdo Renderizado (LCP) de uma página exceder 2,5 segundos no Lighthouse CI, trate-o como um bug bloqueante — faça o perfil com a guia Desempenho do Chrome DevTools e corrija o maior gargalo antes de mesclar.
- Ao adicionar animações, use as propriedades CSS
transformeopacity(apenas compositor) em vez dewidth,heightoutop/leftpara evitar recálculos de layout que causam travamentos. - Quando o aplicativo precisar de suporte offline, registre um service worker com uma estratégia cache-first para ativos estáticos e uma estratégia network-first para requisições de API, recorrendo a respostas em cache quando estiver offline.
- Quando o bundle JS inicial exceder o orçamento (por exemplo, 200 KB gzipados), adicione code splitting baseado em rotas com
React.lazy()ouimport()dinâmico e adie scripts não críticos para abaixo da dobra. - Ao suportar navegadores antigos, defina uma configuração browserslist e deixe a ferramenta de build (Vite, Webpack) aplicar auto-polyfill; não adicione polyfills ou verificações de funcionalidades manualmente, a menos que a cobertura do browserslist seja insuficiente.
- Ao adicionar imagens, use
<picture>com fontes WebP/AVIF e atributos explícitoswidth/heightpara evitar mudança de layout; para imagens abaixo da dobra, adicioneloading="lazy". - Quando uma rota não for necessária no carregamento inicial da página, envolva-a em
React.lazy()(ou equivalente do framework) com um fallback<Suspense>para que o bundle principal exclua o código dessa rota. - Ao servir ativos estáticos, configure o CDN ou servidor para definir
Cache-Control: public, max-age=31536000, immutableem nomes de arquivo com hash de conteúdo eno-cacheemindex.html.
Regras de Decisão sobre Acessibilidade
- Ao construir qualquer componente interativo, adicione atributos ARIA, manipuladores de teclado (
Enter,Espaço,Escapeconforme apropriado) e teste com axe-core antes de marcar a tarefa como concluída. - Ao construir formulários, associe cada
<input>a um<label>viahtmlFor/id, forneça mensagens de erro visíveis vinculadas comaria-describedbye garanta que o formulário seja totalmente operável apenas com navegação por teclado. - Ao usar cor para transmitir significado (por exemplo, estados de erro, emblemas de status), inclua sempre um indicador secundário (ícone, texto, padrão) para que usuários daltônicos possam distinguir os estados.
- Ao adicionar um modal ou dropdown, prenda o foco dentro do elemento enquanto ele estiver aberto e retorne o foco ao elemento de gatilho ao fechar; teste percorrendo todo o fluxo com tabulação sem usar o mouse.
- Quando um pull request adicionar um novo componente interativo, o PR deve incluir um teste de integração com axe-core que afirme zero violações de WCAG 2.1 AA antes de ser mesclado.
Regras de Decisão para Depuração de CSS
Quando um layout quebrar, diagnostique pelo sintoma:
- Filho flexível estoura seu contêiner: Adicione
min-width: 0(linha) oumin-height: 0(coluna) ao filho flexível. Itens flexíveis têm padrãomin-width: auto, que impede a redução abaixo do tamanho do conteúdo. - Itens do grid ignoram a largura da coluna: Use
minmax(0, 1fr)em vez de1fr.1frpuro significaminmax(auto, 1fr), o que permite que o conteúdo empurre a coluna para além do pretendido. position: stickynão fixa: Verifique cada ancestral paraoverflow: hidden,overflow: autoouoverflow: scroll. Qualquer um deles cria um novo contexto de rolagem que contém o elemento sticky. Verifique também se o elemento tem um valortop/bottomdefinido.- Elemento centralizado com
margin: autonão centraliza: Verifique se o elemento tem umawidthexplícita (bloco) ou o pai temdisplay: flex(filho flexível).margin: autonão faz nada em elementos de bloco com largura total. z-indexnão funciona: O elemento precisa deposition: relative/absolute/fixed/stickypara criar um contexto de empilhamento. Verifique também se um ancestral cria um contexto de empilhamento (viatransform,opacity < 1,filterouwill-change) que limita o escopo do z-index.- Espaço entre elementos inline-block/inline: Espaços em branco no HTML criam lacunas. Solução: use
display: flexno pai (preferido) ou definafont-size: 0no pai e redefina nos filhos. - Lacuna de 1px entre elementos adjacentes: Renderização de subpixel. Solução: use
outlineem vez deborderpara depuração, ou definabackgroundno pai para mascarar a lacuna. - Truncamento de texto com reticências não funciona: Requer todos os três:
overflow: hidden,white-space: nowrap,text-overflow: ellipsis. Para truncamento de várias linhas, use-webkit-line-clampcomdisplay: -webkit-boxe-webkit-box-orient: vertical. - Consultas de contêiner não aplicam: Verifique se o ancestral tem
container-type: inline-size(ousize). O elemento em si não pode ser seu próprio contêiner — a consulta deve referenciar um pai.
Regras de Decisão sobre Ferramentas de Build
- Vite (padrão): Use para todos os novos projetos. Servidor de desenvolvimento instantâneo via ESM nativo, HMR rápido, builds de produção baseados em Rollup. Se o projeto usar dependências apenas CommonJS que falham com Vite, adicione
optimizeDeps.includepara esses pacotes — não mude para Webpack. - Webpack: Use apenas para projetos existentes que já o utilizam, ou quando um plugin específico do Webpack não tiver equivalente no Vite (raro). Migre para Vite quando surgir a oportunidade (veja Regras de Decisão de Migração).
- Turbopack: Use apenas dentro do Next.js 14+ (
next dev --turbo). Ainda não utilizável de forma independente. Fornece builds de desenvolvimento mais rápidos que o Webpack para projetos Next.js. Se os builds já forem rápidos (<5s), não se preocupe em mudar. - esbuild: Use para bundling de bibliotecas, scripts ou ferramentas CLI — não para aplicativos completos. Sem manipulação de ponto de entrada HTML, sem code splitting por rota. Use
tsup(wrapper esbuild) para publicar pacotes npm. - SWC vs Babel: Use SWC (via plugin Vite ou padrão Next.js). SWC é 20 a 70 vezes mais rápido para transformações. Use Babel apenas se um projeto depender de um plugin exclusivo do Babel sem equivalente no SWC (cada vez mais raro).
- Ferramentas de monorepo: <5 pacotes = workspaces npm/pnpm, sem ferramenta extra. 5-20 pacotes = Turborepo para cache de tarefas. >20 pacotes ou poliglota = Nx. Nunca adicione ferramentas de monorepo a um projeto de pacote único.
Regras de Decisão sobre SSR e Componentes de Servidor
Quando Usar SSR vs CSR vs SSG
- SSG (Geração de Site Estático): Use para conteúdo que muda menos de uma vez por hora (páginas de marketing, documentação, posts de blog). Compile no momento da implantação. TTFB mais rápido possível.
- SSR (Renderização do Lado do Servidor): Use para conteúdo personalizado (painéis, perfis de usuário), páginas críticas para SEO com dados dinâmicos ou páginas onde dados obsoletos são inaceitáveis. Adiciona latência de servidor a cada requisição — armazene em cache agressivamente com
Cache-Controlou CDN. - CSR (Renderização do Lado do Cliente): Use para páginas apenas autenticadas (painéis de administração, ferramentas internas) onde SEO não importa e a interatividade é a prioridade. Mais simples de construir e implantar (hospedagem estática).
- ISR (Regeneração Estática Incremental): Use para páginas que são principalmente estáticas, mas precisam de atualizações periódicas (listagens de produtos, páginas de preços). Defina o intervalo
revalidatecom base em quanto os dados podem ficar obsoletos: 60s para preços, 3600s para posts de blog.
Regras de Decisão para Componentes de Servidor React (RSC)
- Padrão para Componentes de Servidor: Todo componente é um Componente de Servidor, a menos que precise de interatividade. Se um componente tiver
onClick,useState,useEffectou APIs de navegador — ele deve ser um Componente Cliente ('use client'). - Busca de dados: Busque dados em Componentes de Servidor com
async/awaitdiretamente — semuseEffect, sem TanStack Query, sem estados de carregamento necessários. Os dados são resolvidos antes do HTML ser enviado. - Autenticação em Componentes de Servidor: Leia a sessão/cookie em um Componente de Servidor e passe o objeto de usuário como prop para Componentes Cliente. Nunca leia cookies ou sessões em Componentes Cliente — eles executam no cliente.
- Quando usar Server Actions vs rotas de API: Use Server Actions (
'use server') para envios de formulário e mutações chamadas da interface do usuário. Use rotas de API (/api/*) para webhooks, integrações de terceiros ou endpoints chamados por serviços externos. - Compartilhando estado entre Componentes de Servidor e Cliente: Passe dados serializáveis como props do Servidor → Cliente. Se você precisar de estado do lado do cliente derivado de dados do servidor, inicialize
useStatecom a prop do servidor e gerencie as atualizações no cliente. - Grandes conjuntos de dados: Faça streaming com limites
<Suspense>. Envolva o Componente de Servidor lento em<Suspense fallback={<Skeleton />}>. O shell renderiza imediatamente; o componente lento é transmitido quando estiver pronto. Coloque limites Suspense em seções significativas da UI (barra lateral, conteúdo principal, comentários) — não ao redor de cada componente.
Árvore de Decisão de Erro de Hidratação
- Erro: "Text content does not match": Um valor difere entre a renderização do servidor e do cliente. Causas comuns:
Date.now(),Math.random()ounew Date().toLocaleString()na renderização → mova parauseEffect+useState.- Renderização condicional com
typeof window !== 'undefined'→ useuseEffectpara definir uma flaghasMounted. - Extensões de navegador injetando nós DOM → não é seu bug, mas teste no modo anônimo para confirmar.
- Erro: "Hydration failed because the server rendered HTML didn't match": Uma diferença estrutural (elementos diferentes). Causas comuns:
<p>aninhado dentro de<p>, ou<div>dentro de<p>→ corrija o aninhamento, a especificação HTML não permite.- Um componente de terceiros renderiza de forma diferente no servidor vs cliente → envolva em
dynamic(() => import(...), { ssr: false }). - Tags de fechamento ausentes ou tags de autofechamento onde o HTML exige fechamento explícito.
Fluxo de Trabalho de Depuração de Desempenho
Quando uma página estiver lenta, siga esta sequência — não pule etapas:
Etapa 1: Identifique o tipo de gargalo
- Execute o Lighthouse. Verifique qual métrica está falhando:
- LCP >2,5s: O maior elemento visível carrega muito lentamente. Vá para a Etapa 2.
- FID/INP >200ms: A interação do usuário está bloqueada por JavaScript. Vá para a Etapa 3.
- CLS >0,1: Mudanças de layout após a renderização inicial. Vá para a Etapa 4.
Etapa 2: Corrija o LCP
- Verifique qual é o elemento LCP (o Lighthouse mostra). Geralmente: imagem hero, texto de título ou vídeo.
- Se imagem: Adicione
priority(Next.js) oufetchpriority="high". Adicionewidth/heightexplícitos. Sirva em WebP/AVIF. Se servido por um CDN, verifique os cabeçalhos de cache. - Se texto: Verifique se há fontes que bloqueiam a renderização. Use
font-display: swapoufont-display: optional. Pré-carregue o arquivo de fonte crítico com<link rel="preload" as="font" crossorigin>. - Se bloqueado por JS: O bundle é muito grande. Verifique a Etapa 3. O elemento LCP não pode renderizar até que o JS que o cria seja carregado e executado.
- Verifique o tempo de resposta do servidor (TTFB). Se TTFB >600ms, o gargalo é backend ou CDN — não frontend.
Etapa 3: Corrija o INP (Interação com a Próxima Pintura)
- Abra o Chrome DevTools → Desempenho → grave uma interação de clique/digitação.
- Encontre a Tarefa Longa (barra amarela >50ms). Clique nela para ver a pilha de chamadas.
- Se a tarefa longa for seu código: Divida o trabalho em partes menores com
requestIdleCallback,scheduler.yield()(Chrome 115+) ousetTimeout(fn, 0)para ceder ao navegador entre os quadros. - Se a tarefa longa for re-renderização do React: Abra o Profiler do React DevTools. Encontre o componente que re-renderiza. Correções comuns:
React.memo(filhos caros),useMemo(valores derivados) ou mova o estado para mais perto de onde é usado. Se mais de 500 componentes re-renderizarem em uma única mudança de estado, o estado está muito alto na árvore. - Se a tarefa longa for script de terceiros (analytics, anúncios): Adie com atributo
asyncoudefer, ou carregue apósrequestIdleCallback.
Etapa 4: Corrija o CLS
- Verifique qual elemento muda (o Lighthouse mostra, ou use DevTools → "Layout Shift Regions").
- Imagem/vídeo sem dimensões: Adicione atributos
widtheheight(ouaspect-ratioCSS). O navegador reserva espaço antes do ativo carregar. - Troca de fonte: A fonte de fallback tem métricas diferentes da fonte web. Use
size-adjustem@font-facepara igualar as métricas, ou usefont-display: optionalpara eliminar a troca completamente. - Conteúdo dinâmico injetado acima da dobra: Anúncios, banners, avisos de cookies. Reserve espaço com
min-heightno contêiner. Se a altura do conteúdo variar, usecontain: layoutpara evitar que as mudanças se propaguem. - CSS de carregamento tardio: Se uma folha de estilo carregar após a primeira pintura e alterar o layout visível, incorpore o CSS crítico ou pré-carregue a folha de estilo.
Regras de Decisão sobre Qualidade de Código
- Ao escrever testes, exija que cada componente tenha pelo menos um teste unitário cobrindo seu caminho de renderização principal e um teste de interação (clique, teclado) — aplique via uma porta de cobertura de CI com mínimo de 80% de cobertura de linha.
- Ao iniciar um projeto, ative
strict: truenotsconfig.jsondesde o primeiro dia; adaptar o modo estrito posteriormente é exponencialmente mais difícil à medida que a base de código cresce. - Quando uma chamada de API ou operação assíncrona falhar, exiba uma mensagem de erro voltada ao usuário com uma ação de repetição — nunca engula erros silenciosamente ou mostre texto de exceção bruto.