Entendendo webpack e criando uma aplicação em React com ele

Nesse post vamos falar um pouco de webpack. Falaremos sobre o que é o webpack, para que serve, como configurá-lo e usá-lo e vamos aplicar isso tudo para criar uma aplicação em React utilizando algumas tecnologias mais modernas, como Babel para transpilar, Sass, entre outros.

English version of this post / versão em inglês deste post

O que é o webpack?

O webpack hoje é uma ferramenta muito usada em diversos projetos de Javascript. Vamos então responder a nossa primeira importante pergunta. O que é o webpack? Bem, o webpack é um bundler de código. Beleza, e o que é um bundler? Um bundler junta vários módulos, com todas as dependências necessárias, em um ou alguns poucos módulos, criando o chamado “dependency graph”.

Você pode então usar “require” ou “import” para utilizar os módulos, arquivos estáticos (arquivos CSS, imagens, por exemplo) e o que mais você precisar, aonde forem necessários. Além disso, você também pode processar arquivos para utilizar, por exemplo, Sass em seus estilos ou Babel, para usar as funções mais novas do Javascript que ainda não são 100% suportadas por todos os navegadores.

Instalação

Vou considerar que você já tenha o “NodeJS” instalado. Caso não tenha, clique aqui para verificar como instalar no seu sistema operacional.

Em primeiro lugar, vamos usar o “npm init” para criar o arquivo “package.json” e dar início ao nosso projeto. Este arquivo reúne diversas informações gerais sobre o projeto, como nome, versão, além das dependências do projeto com relação a outras libraries e pacotes. Assim, é possível instalar todas as dependências executando apenas “npm install” na pasta onde este arquivo se encontra. Pode dar um nome para o projeto e o restante apertar Enter para todas as opções. Ao final, digite “yes” para confirmar que está tudo correto.

npm init

A instalação do webpack é bem simples. Podemos instalar através do “npm install”. Vamos usar a opção “–save-dev” que atualiza o “package.json” com as dependências de development.

npm install webpack@2 --save-dev

A versão do webpack que utilizarei nos exemplos é a versão 2, então fique atento pois muitas configurações são diferentes da versão 1 para a 2.

Começando a usar

Para usufruir do webpack, precisamos criar um arquivo com o nome “webpack.config.js”, na pasta raiz do seu projeto (a mesma onde está o arquivo “package.json”). Este arquivo definirá todas as configurações necessárias ao webpack. Neste arquivo, basicamente, definimos um objeto, onde cada key representa uma determinada configuração do webpack. Partiremos do seguinte código:

module.exports = {

}

Agora, vamos começar a parte legal, que é como o webpack realmente funciona. Basicamente, o webpack é apoiado em quatro conceitos principais. Vamos a eles.

Conceito 1: Entry (Entrada)

Para criar o “dependency graph” da sua aplicação, o webpack precisa de um ponto de entrada. Este ponto de entrada seria o arquivo, ou os arquivos, iniciais, a partir dos quais ele vai buscando aquilo que ele deve empacotar. O “entry” é um array, onde cada item é um arquivo de entrada.

Vamos definir em nossa configuração o arquivo de entrada “app.jsx” (vamos usar .jsx para já preparar a criação de uma aplicação React), que ficará dentro da pasta “app” que criaremos mais a frente:

module.exports = {
    entry: [
        './app/app.jsx'
    ],
}

Conceito 2: Output (Saída)

Definimos a entrada, e agora precisamos definir a saída. No “output”, definimos onde o webpack deve colocar o pacote que ele vai gerar através da sua entrada. Esta definição é um objeto, com as chaves “path”, que será a pasta onde o pacote será colocado, e “filename”, que, obviamente, será o nome do arquivo. Vamos ao exemplo, onde o pacote gerado terá o nome de “bundle.js” e ficará na pasta “public”, relativa à pasta onde o arquivo de configuração se encontra. Lembre-se de importar o “path” com o comando “require” para utilizá-lo:

var path = require('path');

module.exports = {
    entry: [        
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    }
}

O output possui uma série de outras configurações possíveis, mas por enquanto vamos ficar com estas duas, as mais básicas e necessárias.

Conceito 3: Loaders

O webpack trata cada arquivo de sua aplicação como módulo, e ele entende que todos os recursos do seu site devem ser problema dele, e não do navegador. Desta forma, os loaders servem para transformar cada arquivo em módulos, para que possam ser importados e utilizados ao longo do seu projeto. A configuração dos loaders serve para:

  • Identificar o que deve ser transformado por um determinado loader
  • Transformar este arquivo para que possa ser adicionado ao “dependency graph”

A configuração dos loaders é feita dentro da chave “rules”, que ficará dentro da chave “modules”. Dentro desta chave, definimos na propriedade “test” o que deve ser transformado, e dentro da propriedade “use”, o que será usado para transformar estes determinados arquivos. Vejamos um exemplo onde usaremos o “babel-loader” para transpilar todos os arquivos .js e .jsx de nosso projeto. Vamos usar os presets “babel-preset-react”, “babel-preset-es2015” e “babel-preset-stage-0”. Por fim, o pacote “babel-core” também é necessário. Desta forma, teremos acesso à novas funções do Javascript que ainda não são suportadas por todos os browsers, toda a funcionalidade do React e mais. Primeiro, precisamos instalar todos estes pacotes através do npm:

npm install --save-dev babel-loader babel-preset-react babel-preset-es2015 babel-preset-stage-0 babel-core

Agora, faremos a configuração no arquivo “webpack.config.js”:

var path = require('path');

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },            

        ]
    }
};

Reparem que usamos a propriedade “exclude” para indicar que não deve ser considerado pelo loader a pasta “node_modules”, que é onde ficam instalados os módulos do projeto instalados através do “npm”.

Vamos entender o que fizemos acima. O webpack utiliza o regex definido na propriedade “test” para definir os arquivos que serão transformados, e então usa o “babel-loader” com as opções definidas na propriedade “options”, que neste caso particular, serão os presets do Babel (mais sobre eles aqui) que escolhemos para usar em nosso projeto.

Da mesma forma, existe o “style-loader”, onde podemos definir que arquivos “.scss” sejam transformados para que você possa utilizar Sass em seu projeto. Para este caso, precisamos dos pacotes “style-loader”, “css-loader” e “sass-loader”. O “style-loader” adiciona CSS no DOM, o “css-loader” interpreta “imports” e “url()” nos arquivos, e o “sass-loader” compila o Sass para CSS a ser interpretado pelo navegador. O “sass-loader” necessita do pacote “node-sass”, então também vamos instalá-lo:

npm install --save-dev style-loader css-loader sass-loader node-sass

Agora, vamos adicionar a configuração ao nosso arquivo “webpack.config.js”:

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },
            {
                test: /\.scss$/,
                exclude: [/node_modules/],
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader"
                }]
            }
        ]
    }
};

Neste caso, como podem perceber, usamos o loader três vezes. É importante que sejam aplicados nesta ordem. Agora você pode fazer o require de um arquivo “.scss” e usar Sass em seu projeto. Mais a frente daremos um exemplo simples com uma aplicação React.

Conceito 4: Plugins

Enquanto os “loaders” fazem transformação em arquivos individuais, os plugins são normalmente usados para atuar em pedaços do seu código já “empacotado”. Um exemplo vindo da própria documentação do webpack é o “webpack.optimize.UglifyJsPlugin()”. Este plugin, como o nome já indica, minimiza todo o código Javascript, fazendo o chamado “Uglify”. Os plugins no webpack são definidos dentro da chave “plugins”. Para acessar os plugins do Webpack, você deve importar o webpack neste arquivo com o comando “require”. Vejamos como fica no arquivo de configuração do webpack:

var path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: [
        './app/app.jsx'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'public')
    },    
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/, 
                exclude: [/node_modules/],
                use: [{
                    loader: 'babel-loader',
                    options: { presets: ['react', 'es2015', 'stage-0'] }
                }],
            },
            {
                test: /\.scss$/,
                exclude: [/node_modules/],
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader"
                }]
            }
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]
};

Para uma lista mais completa de plugins do webpack, dá uma olhada aqui.

Bem, agora já temos tudo configurado. Vamos ver como fica nossa aplicação em React.

webpack com React

Em primeiro lugar, vamos usar o npm para instalar o “React” e o “React-DOM”, que são os pacotes básicos para se trabalhar com React. Neste caso usamos a opção –save para atualizar o arquivo “package.json” com as dependências de produção.

npm install --save react react-dom

Vamos aproveitar e instalar o “http-server” (link do repo) para servir nossos arquivos e testar nosso projeto localmente. Nesse caso, podemos usar a opção “-g” instalar globalmente, pois você pode usar o “http-server” para testar qualquer outro projeto.

npm install -g http-server

Agora, vamos criar a estrutura do nosso projeto React. Vou usar a estrutura que costumo utilizar em meus projetos. Vamos criar uma pasta “app”. Nela, ficará o nosso arquivo de entrada, o “app.jsx”, conforme especificamos na configuração do webpack. Neste arquivo, teremos um código básico simples para importar o component “MainApp”, que criaremos no próximo passo, e renderizá-lo em sua aplicação. Também incluiremos um “require” para o arquivo de estilos “app.scss”, que também criaremos um pouco mais a frente. Este arquivo de estilos importará outros arquivos, não havendo a necessidade de voltar no arquivo de entrada para importar arquivos “.scss” um por um.

import React from 'react';
import ReactDOM from 'react-dom';

import MainApp from './components/MainApp.jsx';

// App Styles
require('./styles/app.scss')

ReactDOM.render(
  <MainApp />,
  document.getElementById('app')
);

Dentro dela também criaremos a pasta “components”, onde, como o nome já indica, ficarão nossos componentes. Aí, criaremos um componente simples, cujo nome será “MainApp.jsx”, com o código básico para um componente React:

import React, { Component } from 'react';

class MainApp extends Component {
    render() {
        return (
            <div>
                <p>MainApp component</p>
            </div>
        );
    }
}

export default MainApp;

Agora, criaremos a pasta “styles”, que também ficará dentro da pasta “app” e irá conter os arquivos “scss”, responsáveis pelo estilo da aplicação. Aí, podemos criar um arquivo “app.scss”, que será o arquivo central de estilos. Vamos apenas definir uma cor para o “body” e uma para os elementos “p”, para checar se está tudo ok. Incluí o seletor do “p” dentro do seletor do “body” para mostrar como estamos usando Sass, visto que este “nesting” não é permitido em CSS tradicional. Vou também importar um arquivo específico para o componente “MainApp”:

@import "components/MainApp";

body {
  background-color: red;
  p {
    color: white;
  }
}

Dentro da pasta “styles”, criamos a pasta “components”. Nesta pasta, podemos criar um arquivo “.scss” específico para cada componente. Esta estrutura permite uma boa separação e organização do seu código. Neste caso, então, vamos criar o “_MainApp.scss”. Repare no underscore no início do nome do arquivo, necessário para a correta importação conforme definido no arquivo “app.scss” que acabamos de criar. Vou definir apenas um estilo para o parágrafo, para verificarmos que a importação foi feita corretamente.

p {
  text-decoration: underline;
}

Por fim, precisamos criar a pasta “public”. Nesta pasta estarão os arquivos que serão servidos para o http-server, e é onde o webpack salvará o bundle com todo o seu código. Vamos então criar o arquivo “index.html”. É um arquivo HTML simples, importando o arquivo “bundle.js” que estará na mesma pasta que ele.

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8"/>
</head>

<body>
    <div id="app"></div>

    <script src="bundle.js"></script>
</body>

</html>

Vamos agora executar o webpack. Basta executar o comando “webpack” na raiz do seu projeto. O mesmo tomará alguns segundos e então mostrará alguns avisos. Se tudo der certo, você pode verificar na pasta public que o arquivo “bundle.js” foi criado. A cada mudança feita no código do seu projeto, você tem de rodar o comando novamente. Para evitar isso, você pode rodar o comando “webpack -w”. Esse comando irá “vigiar” (w, de watch) os arquivos, e a cada mudança feita em seu código, ele irá atualizar o arquivo “bundle.js”. Finalmente, vamos agora executar o comando “http-server” e verificar se está tudo certo. A porta padrão do pacote “http-server” é a porta 8080, então, acesse http://localhost:8080/; se tudo der certo, você deve ver uma página, mais ou menos como a da imagem abaixo:

Tela da aplicaçao em React funcionando

A combinação de cores ficou bem feia, mas como você pode ver, o componente MainApp foi devidamente renderizado. Os estilos foram aplicados, tanto os definidos no arquivo “app.scss” (cores do parágrafo e fundo do body) quanto os aplicados no arquivo “_MainApp.scss” (parágrafo sublinhado).

Isso significa que tudo deu certo. Agora você tem uma aplicação em React, com seu código “empacotado” pelo Webpack e usando as funcionalidades mais modernas do Javascript, além de Sass para seus estilos. Resumindo nossa estrutura de pastas e arquivos, fica da seguinte forma:

├── app
│   ├── app.jsx
│   ├── components
│   │   └── MainApp.jsx
│   └── styles
│       ├── app.scss
│       └── components
│           └── _MainApp.scss
├── package.json
├── public
│   ├── bundle.js
│   └── index.html
├── understanding-webpack-tree.txt
└── webpack.config.js

Note que existem muitas estruturas e formas de organização que funcionam, e esta é apenas uma que eu uso e que funciona bem para mim. Então, não assuma isso como alguma forma de “resposta correta”, pois nesse caso, ela não existe 🙂

Espero que tenha ajudado. Estou sempre aberto à correções e sugestões.

O código deste post pode ser encontrado no seguinte repositório do Github: https://github.com/felipegalvao/understanding-webpack

Abraços

One comment on “Entendendo webpack e criando uma aplicação em React com ele

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *