Logo

Criando uma aplicação em React sem create-react-app

Postado em: 07/07/2022

Criando uma aplicação em React sem create-react-app

Fala pessoal! Depois de um tempinho sem voltar para escrever um post, resolvi trazer um ponto que é bem importante. Hoje em dia temos vários frameworks que trazem aplicações já configuradas prontas para desenvolver, porém, as vezes a maioria das funcionalidades que vem nesses frameworks não são usadas. Então, nesses casos pode valer a pena implementar uma aplicação mais simples do zero.

Para quem quiser dar uma olhada direto no repositório, é esse aqui. Sintam-se livres para fazer um fork e contribuir :)

Sem mais delongas vamos ao que interessa.

Estrutura do projeto

A estrutura é bem simples:

— public/
  — index.html
— src/
  — App.js
  — index.js
— webpack/
  — common.js
  — development.js
  — production.js
— .babelrc
— .eslintignore
— .eslintrc
— .prettierignore
— .prettierrc
— package.json

Explicando:

  • public: pasta para os arquivos estáticos da aplicação
  • src: pasta raiz onde ficam os componentes
  • webpack: pasta para os arquivos de configuração do webpack
  • .babelrc: arquivo de configuração do babel
  • .eslintignore e .eslintrc: arquivos de configuração do eslint
  • .prettierignore e .prettierrc: arquivos de configuração do prettier
  • package.json: arquivo de configuração com as dependências do projeto e os scripts automatizados

Primeiro vamos criar uma pasta para o nosso projeto e dentro dela vamos adicionar essa estrutura que mostrei. Por hora vamos só criar as pastas e arquivos e ao longo do post vamos adicionando as configurações de cada um.

Configurando o Webpack

Vamos começar configurando o Webpack para a aplicação, então vamos adicionar algumas dependências no nosso projeto.

yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/core @babel/runtime @babel/plugin-transform-runtime @babel/runtime @babel/preset-env @babel/preset-react @babel/eslint-parser eslint @pmmmwh/react-refresh-webpack-plugin react-refresh

Após instaladas vamos configurar nossos três arquivos do webpack:

  • common.js: arquivo com configurações mais gerais, que entram tanto para o build de produção quanto para o de desenvolvimento.
  • development.js: arquivo com as configurações para rodar o ambiente de desenvolvimento.
  • production.js: arquivo com as configurações de gerar o build para produção.

No arquivo common.js vamos adicionar o seguinte trecho de código:

const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')

module.exports = {
  // entrypoint da aplicação
  entry: path.join(__dirname, '..', 'src', 'index.js'),

  // diretório onde os arquivos serão salvos depois de compilados e nome do bundle
  output: {
    path: path.resolve(__dirname, '..', 'dist'),
    filename: 'main.js'
  },

  // target 'web' indicando que será uma aplicação que rodará no client
  target: 'web',

  // extensões que serão lidas
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  },

  // configura o módulo do babel para compilar os arquivos javascript excluindo a pasta node_modules
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },

  // adiciona o plugin HtmlWebpackPlugin que é responsável por compilar o arquivo HTML conforme o necessário
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '..', 'public', 'index.html')
    })
  ]
}

No arquivo development.js vamos adicionar:

// fazemos o require do módulo para o fast refresh (hot reloading)
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
// fazemos um require do módulo common.js que criamos no outro arquivo
const common = require('./common')

// adicionamos o módulo de fast refresh nos plugins
common.plugins.push(new ReactRefreshPlugin())

module.exports = {
  // fazemos um destructuring das configurações no common.js
  ...common,

  // indicamos o modo que será rodado 'development'
  mode: 'development',

  // adicionamos a configuração do servidor de desenvolvimento
  devServer: {
    port: '3000',
    static: ['../public'],
    open: true,
    hot: true
  }
}

E por último o arquivo production.js é mais simples, nele só adicionamos:

const common = require('./common')

module.exports = {
  ...common,

  // indicamos que o modo é para build de produção
  mode: 'production'
}

Agora que configuramos o nosso webpack, ainda tem mais um arquivo que precisamos configurar, então vamos adicionar esse trecho de código no .babelrc.

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

Adicionando o React

Agora vamos adicionar o React ao nosso projeto, para isso vamos rodar:

yarn add react react-dom

Depois disso vamos atualizar três arquivos, o public/index.html, src/index.js e src/App.js.

No index.html vamos adicionar um html bem minimalista:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Simple React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

No nosso index.js é onde a aplicação vai ser iniciada, então nele colocamos:

import React from 'react'
import { createRoot } from 'react-dom/client'

import App from './App'

const container = document.getElementById('root')
const root = createRoot(container)

root.render(<App />)

E no App.js vamos apenas configurar um componente simples, apenas para importar no nosso index.js e ver funcionando.

import React from 'react'

const App = () => <h1>Hello World!</h1>

export default App

Agora vamos adicionar os scripts no nosso package.json:

"scripts": {
  "start": "webpack-dev-server --config webpack/development",
  "build": "webpack --config webpack/production"
}

Após isso ao rodar yarn start nós teremos um servidor de desenvolvimento rodando, e ao rodar yarn build será feito o build para produção dentro da pasta dist. E com isso já temos nossa aplicação pronta para ser desenvolvida. Um ponto que faltou e talvez estejam se perguntando. Mas e os estilos? Como fazer?

Bem, para isso podemos utilizar uma solução bem simples que funciona muito bem para essa aplicação e não precisaremos configurar um loader para fazer o parse do css. Essa solução é o styled-components, assim nós utilizamos uma técnica chamada css in JS, para utilizarmos é bem simples. Vamos primeiro instalar o pacote com o comando:

yarn add styled-components

Após isso vamos criar um arquivo novo dentro da pasta src chamado styles.js e adicionamos:

import styled from 'styled-components'

export const Title = styled.h1`
  font-size: 40px;
  color: #333;
`

E depois alteramos o nosso App.js um pouco e já teremos nossos componentes completamente estilizados.

import React from 'react'
import * as S from './styles'

const App = () => <S.Title>Hello World!</S.Title>

export default App

Configurando eslint + prettier

Bom, além de termos nossa aplicação já funcional, é sempre interessante mantermos o padrão do código e podemos combinar o prettier que faz muito bem essa parte com o eslint que nos ajuda a encontrar problemas no nosso código.

Para configurá-los vamos adicionar mais alguns pacotes:

yarn add -D eslint-config-prettier eslint-plugin-prettier eslint-plugin-react prettier

Nos arquivos .eslintignore e .prettierignore vamos adicionar os arquivos que não vão ser levados em consideração por essas ferramentas.

node_modules
yarn.lock
dist

Agora a configuração do .eslintrc:

{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:prettier/recommended"],
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": ["react", "prettier"]
}

E por último o .prettierrc:

{
  "printWidth": 100,
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false,
  "trailingComma": "none"
}

E pronto! Agora temos uma aplicação bem simples configurada e que segue padrões de código. Estou colocando as versões de cada pacote que foi utilizado para esse post.

"dependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "styled-components": "^5.3.5"
},
"devDependencies": {
  "@babel/core": "^7.18.5",
  "@babel/eslint-parser": "^7.18.2",
  "@babel/plugin-transform-runtime": "^7.18.5",
  "@babel/preset-env": "^7.18.2",
  "@babel/preset-react": "^7.17.12",
  "@babel/runtime": "^7.18.3",
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
  "babel-loader": "^8.2.5",
  "eslint": "^8.18.0",
  "eslint-config-prettier": "^8.5.0",
  "eslint-plugin-prettier": "^4.1.0",
  "eslint-plugin-react": "^7.30.1",
  "html-webpack-plugin": "^5.5.0",
  "prettier": "^2.7.1",
  "react-refresh": "^0.14.0",
  "webpack": "^5.73.0",
  "webpack-cli": "^4.10.0",
  "webpack-dev-server": "^4.9.2"
}

Espero que esse post tenha sido útil e que tenham gostado! Logo mais eu volto com o próximo post trazendo mais conteúdo. Até mais!

Ver todos os posts