Skip to content
This page has been auto-translated and may contain errors.View in English

Configurando um projeto React

Um app React precisa de um pouco de ferramental ao redor dele antes que qualquer código seja executado. Algo precisa transformar JSX em JavaScript que um navegador consiga executar, e algo precisa servir o resultado enquanto você trabalha nele. O Vite faz as duas coisas e cria um projeto funcional em cerca de trinta segundos. Este capítulo vai de uma pasta vazia até um app rodando, e depois rastreia o caminho que mais importa: como um componente acaba aparecendo na tela.

Criando um projeto

Vite é uma ferramenta de build com um gerador de projetos embutido. Execute-o com npm create, responda dois prompts e você terá um projeto:

bash
npm create vite@latest my-react-app
cd my-react-app
npm install
npm run dev

O primeiro comando pergunta qual framework você quer (escolha React) e qual variante (escolha JavaScript ou TypeScript se estiver usando). Ele cria uma pasta chamada my-react-app com tudo que um projeto React precisa. npm install baixa as dependências listadas em package.json, de onde vem o próprio React. npm run dev inicia o servidor de desenvolvimento e imprime um endereço local, geralmente http://localhost:5173. Abra isso em um navegador e o app inicial está rodando.

A pasta gerada tem alguns arquivos que importam no início:

  • index.html: a página HTML única que o navegador carrega.
  • src/main.jsx: o ponto de entrada, onde React se liga a essa página.
  • src/App.jsx: seu componente de nível superior, e o arquivo que você vai editar primeiro.
  • src/assets/: imagens e outros arquivos estáticos que você importa em componentes.
  • package.json: dependências e os scripts dev, build e preview.
  • vite.config.js: configuração de build, que você pode deixar de lado por um tempo.

Como as peças se conectam

Três arquivos se entregam um ao outro em linha reta. Comece por index.html, que é propositalmente quase vazio:

html
<body>
  <div id="root"></div>
  <!-- followed by a <script type="module" src="/src/main.jsx"> tag -->
</body>

Aquele <div id="root"> vazio é o lugar na página onde React é autorizado a preencher. Tudo que seu app renderiza vai dentro dele. A tag <script> do módulo abaixo carrega main.jsx, que é onde React assume:

jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
)

Leia essa chamada da direita para a esquerda. document.getElementById('root') encontra a div vazia. createRoot a envolve em uma raiz React, ou seja, React agora é dono do conteúdo desse elemento e vai mantê-lo sincronizado com seus componentes. .render(...) diz a ele o que colocar lá: o componente App.

<StrictMode> é um wrapper apenas para desenvolvimento que executa verificações extras em tudo dentro dele e avisa sobre padrões que costumam causar bugs. Não adiciona nada à página e se desativa em um build de produção. Seu efeito mais visível aparece em Effects, onde deliberadamente monta cada componente duas vezes durante o desenvolvimento.

App.jsx é um componente comum que se exporta:

jsx
export default function App() {
  return <h1>Hello from React</h1>
}

Essa é toda a cadeia. O navegador carrega index.html, o script carrega main.jsx, main.jsx chama createRoot na div root e renderiza <App />, e App retorna o JSX que vira o heading que você vê. Todo componente que você escrever daqui em diante fica em algum lugar dentro de App, então chega à página através desse mesmo caminho. main.jsx geralmente é escrito uma vez e raramente é tocado de novo; quase todo seu trabalho acontece em App.jsx e nos componentes que ele puxa.

Nota de versão

React 17 e anteriores usavam ReactDOM.render(<App />, document.getElementById('root')) como ponto de entrada. React 18 introduziu createRoot, que é o que liga a renderização concorrente, e deixou a chamada antiga funcionando com um aviso de descontinuação. React 19 removeu, então ReactDOM.render agora lança um erro. Em um tutorial que ainda usa, createRoot é a linha para escrever no seu lugar. Veja History and versions para o que mudou.

O servidor de desenvolvimento

npm run dev inicia o servidor de desenvolvimento do Vite, e ele continua rodando no terminal enquanto você trabalha. Seu trabalho é servir seu app e reagir às suas edições.

Salve uma mudança em App.jsx e o navegador atualiza quase imediatamente, sem um refresh manual. Isso é hot module replacement: Vite empurra o módulo alterado para a página e o substitui no lugar, deixando o resto do app rodando. Um contador que você tinha clicado até sete geralmente ainda marca sete após a edição, então você mantém qualquer estado que tenha configurado enquanto ajusta o markup ao redor. Algumas mudanças ainda forçam um reload completo, e Vite decide isso por você.

Dois outros scripts vêm com o projeto. npm run build produz um bundle otimizado em uma pasta dist, que é o que você faz deploy. npm run preview serve esse output construído localmente para você verificar antes de enviar. Durante o desenvolvimento, npm run dev é o único que você precisa.

Importando ativos estáticos

Imagens ficam em src/assets e entram em um componente através de um import:

jsx
import logo from './assets/logo.png'

export default function Header() {
  return <img src={logo} alt="Company logo" />
}

O import te dá uma variável com a URL final daquela imagem, que você então passa a src entre chaves. Escrever src="./assets/logo.png" como uma string simples tende a quebrar, porque os caminhos na sua pasta de origem raramente são os caminhos no output construído.

Importar o arquivo em vez disso coloca a ferramenta de build no loop. Ela vê que seu código depende de logo.png, copia para o output e te devolve qualquer URL em que aquele arquivo acabe. Mova seu componente para uma pasta diferente e o import relativo continua resolvendo. Digite errado o nome do arquivo e você fica sabendo imediatamente, porque o import falha, em vez de descobrir uma imagem quebrada em produção. O mesmo padrão funciona para SVGs, fontes e qualquer outro arquivo que a ferramenta de build saiba lidar.

Um navegador não tem ideia do que é um arquivo .jsx. Não há parser de JSX em nenhum mecanismo JavaScript, e <App /> é um erro de sintaxe em JavaScript simples. O que o navegador recebe já foi transformado. Vite passa cada arquivo .jsx pelo esbuild, que reescreve o JSX em chamadas de função: sob o runtime automático do React 19, <App /> se torna uma chamada como _jsx(App, {}). Qual helper você pega depende do modo. A transformação de produção importa jsx de react/jsx-runtime, enquanto a transformação de desenvolvimento importa jsxDEV de react/jsx-dev-runtime e passa argumentos extras carregando o arquivo de origem e número da linha, é assim que um aviso React em seu console pode apontar o lugar exato em seu código. A extensão .jsx é principalmente um sinal para ferramental que este arquivo contém sintaxe que precisa daquela transformação. Na hora em que qualquer coisa chega à página, são módulos JavaScript comuns chamando funções comuns.

O servidor de desenvolvimento e o build de produção pegam rotas diferentes para esse resultado. Em desenvolvimento, Vite serve módulos ES nativos e transforma cada arquivo sob demanda, então o tempo de startup cresce muito pouco com o tamanho do projeto e uma única edição só reinvalida aquele módulo. npm run build muda para Rollup, que agrupa o gráfico inteiro, tree-shaking de exports não usados, minifica e divide código em chunks. O comportamento pode diferir levemente entre os dois, que é por que npm run preview existe.

O import de imagem funciona porque o bundler trata arquivos não-JavaScript como parte do gráfico de módulos também. import logo from './assets/logo.png' não é um import de módulo JavaScript real. Vite o intercepta, emite o arquivo em dist/assets com um hash de conteúdo no nome, e substitui o import com um string literal daquele caminho final, algo como /assets/logo-4f2a1c8b.png. O hash é o que torna o cache agressivo seguro: mude a imagem e o nome do arquivo muda com ela, então nenhuma cópia antiga sobrevive em um CDN. Arquivos abaixo de um limiar de tamanho (4KB por padrão) pulam o request separado inteiramente e ficam inlined como uma data URL base64. Em desenvolvimento o mesmo import resolve para um caminho simples servido pelo servidor de desenvolvimento, que é por que a URL que você vê em devtools difere entre npm run dev e um build real.

JunoUm caminho da página até seu componente A configuração são quatro comandos, e depois você pode esquecer disso.

O que vale a pena guardar é a cadeia: index.html tem uma div vazia com o id root, main.jsx pega aquela div com createRoot e renderiza <App /> nela, e App.jsx é o componente que diz o que aparece.

Uma vez que você consegue seguir aquele caminho, você sabe exatamente onde seu próprio código se encaixa.

JunoUm caminho da página até seu componentenpm create vite@latest, escolha React, instale, npm run dev.

De lá é index.html para main.jsx para App.jsx: o arquivo de entrada chama createRoot na div root uma vez e renderiza seu componente de topo, e você passa o resto do tempo abaixo daquela linha.

Importe imagens em vez de codificar caminhos para que a ferramenta de build resolva e coloque um fingerprint nelas para você.

JunoUm caminho da página até seu componente A configuração existe porque JSX não tem runtime: esbuild reescreve <App /> em chamadas de runtime, jsx de react/jsx-runtime em um build e jsxDEV de react/jsx-dev-runtime em desenvolvimento, antes de qualquer coisa chegar ao navegador, e imports de assets resolvem para URLs com hash que o bundler emite.

Dev roda módulos ES não-bundled pelo esbuild, produção roda Rollup, então trate npm run preview como a verificação de que os dois concordam. createRoot é o ponto de entrada React 18, e é o que opta sua árvore em renderização concorrente.

Próximo: Components, os blocos de construção que você vai preencher aquele App.jsx com.