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.