Você está na página 1de 30

WEBPACK SURVIVE

NOTAS DE ESTUDO

BLOG OFICIAL DO WEBPACK


https://medium.com/webpack

INTRODUÇÃO AO WEBPACK
Informações iniciais sobre o que é o Webpack e o que ele pode fazer por você. Ainda não vi em
nenhum lugar informações obre a história do Webpack para traçar uma linha originária. Mas a
intenção dele e seu objetivo já estão bem claros.

O objetivo do livro é ser uma documentação do ponto de vista do desenvolvedor, criando uma
lógica de explanação diferente da documentação. Essa abordagem não exclui a documentação,
pois é apenas pate dela, recomendando a documentação para aprofundamentos depois da
leitura do livro.

SOBRE O WEBPACK
Desde a Guerra dos Browsers, construir programas escritos em JavaScript que funcione bem
em diferentes gerações de navegadores sempre foi um grande desafio. O Webpack torna tudo
mais fácil, traduzindo o código mais recente da linguagem JavaScript ES6 para a versão ES5
(vanila javascript) da mesma linguagem. Tornando sua aplicação útil e funcional para uma
grande variedade de navegadores.

A ORGANIZAÇÃO
Essa organização é mais ou menos o que eu estou procurando para criar o livro sobre o
Webpack.

1. Foco no trabalho como desenvolvedor, tratando atualização automática do navegador e


uma configuração escalável para acompanhar o crescimento da aplicação.
2. Trabalhando com CSS e estilos.
3. Os carregadores (loaders) para trabalhar com imagens, fontes e vários outros recursos
4. O Build, mostrando como modularizar e organizar a construção de aplicações com
Webpack
5. Otimização de cóigo
6. Como lidar com o pacote gerado (saída)
7. Técnicas diversas, como carregamento dinâmico, web workers, internacionalização,
implantação e etc.
8. Indo além do Webpack usando loaders e plugins.

O apêndicce sobre solução de problemas é algo interessante para iniciantes em Webpack.


DESENVOLVIMENTO
Aviso antes de começar… você deve ter instalado o nodejs, npm ou yarn. É possível utilizar o
webpack com Docker, Vargrant ou nvm, sendo o Docker mais recomendado para rodar o lado
servidor de suas aplicações.

CONFIGURANDO UM PROJETO
Todo o projeto javascript é determinado pelo arquivo package.json, que serve como gerenciador
de dependências de sua aplicação. Segue os comandos:

mkdir webpack-demo
cd webpack-demo
npm init -y # gera o package.json com as configurações padrões.

INSTALANDO O WEBPACK
Instalar ele globalmente é sempre recomendado

npm install webpack -g

Caso contrário, você pode instalar o Webpack localmente no projeto

npm install webpack webpack-cli --save-dev

PACKAGE.JSON
Antes de fazer qualquer coisa com webpack, é preciso:

1. Configurar a execução do Webpack no package.json


“dev”: “webpack –mode development”

2. Criar o arquivo que será usado como ponto de entrada, o arquivo que será executado quando
a aplicação for executada. Basta criar o arquivo ./src/index.js com o código abaixo:
console.log(‘olá mundo’);

3. Configurar a execução do ponto de entrada no package.json.


“start”: “node ./src/index.js”

Feito isso, basta executar o comando: “npm run dev” e “npm start” para executar o aplicação.
COMO TESTAR A APLICAÇÃO NO NAVEGADOR?
Até o momento, a aplicação funciona apenas no console. É possível fazer
isso criando uma página HTML para execução de seu ponto de entrada,
o arquivo index.js.
1. Crie o arquivo index.html dentro da pasta ./dist/ com o código abaixo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="main.js"></script>
<!-- inclusão do ponto de entrada gerado pelo webpack -->
</body>
</html>

2. Instale o serve, um servidor para prover páginas HTML.


npm install serve –save-dev
3. Configure o package.json para servir a página HTML da pasta ./dist
“serve”: “serve ./dist”,
4. Abra um novo console na pasta do projeto e use o comando:
npm run serve # levanta o servidor disponibilizando a página index.html
5. Por fim, abra o navegador e use a URL do serve
(http://localhost:5000) para entrar na página da pasta ./dist.
Sempre que fizer uma alteração no arquivo javascript, é preciso executar
o comando “npm run dev” e recarregar a página do navegador.

GERANDO UM HTML VIA PLUGIN


Para gerar um novo HTML via plugin, basta instalar o html-webpack-
plugin e criar o arquivo de configuração básico webpack.config.js com os
códigos abaixo:
npm install –save-dev html-webpack-plugin
const HtmlWebPackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
plugins: [
new HtmlWebPackPlugin({
title: 'my webpack html',
}),
],
};

Depois disso, basta executar o npm run dev para recriar o main.js e o
index.html dentro da pasta ./dist. Em seguida, executar o npm serve,
para levantar o servidor que fornece o HTML. Note que, se você apagar
o index.html, ele será gerado novamente pelo plugin do Webpack.
Você vai encontrar mais informações sobre esse plugin aqui:
https://github.com/jantimon/html-webpack-plugin#plugins
Esse aqui também é interessante:
https://www.npmjs.com/package/html-webpack-template

WEBPACK-DEV-SERVER
É um mini-servidor desenvolvido para facilitar o desenvolvimento,
rodando completamente em memória. Isso significa que o pacote/bundle
usado pelo WDS não estará na pasta dist, estará apenas em memória
RAM. Esse é um fator importante na hora de depurar e corrigir código
JavaScript, HTML e CSS.
Ter a execução do servidor em memória também é importante para
refletir todas as alterações de código no navegador sem precisar
recarregar a tela. Isso mesmo, enquanto você desenvolve sua aplicação, o
WDS recarrega a tela para exibir o efeito das alterações sem que você
precise recarregar a página.
Outro fator importante de rodar em memória é a velocidade de resposta.
Para você construir sua aplicação de modo mais rápido e prático, é
necessário ter uma resposta rápida sobre os efeitos das alterações de
código no navegador. Como o WDS executa tudo em memória, seu
tempo de resposta é muito eficiente.
Caso deseje integrar o WDS com outro servidor web como Apache ou
Nginx, você pode usar o plugin write-file-webpack-lugin. Ele
simplesmente gera o pacote de distribuição em arquivo em cada alteração
de código. Isso é bom para os servidores tradicionais que sempre
esperam arquivos em disco.
INSTALANDO O WDS
npm install webpack-dev-server –save-dev

Depois de instalado, é preciso modificar a execução do package.json para


desenvolvimento ou “start”. Ficando como mostrado abaixo:
"scripts": {
"start": "webpack-dev-server --mode development",
},

Por padrão o servidor roda no endereço http://localhost:8080. Ao modificar seu código, você
percebe que as alterações são exibidas no navegador.

CONFIGURAÇÃO MANUAL DO WEBPACK-DEV-SERVER


devServer: {
stats: 'errors-only',
overlay: true, // tela preta com erros, caso eles ocorram
host: process.env.HOST, // localhost
port: process.env.PORT, // 8080
open: true, // abre o navegador.
},

USANDO DIFERENTES CONFIGURAÇÕES DO WEBPACK


Diferente do livro, que usa plugins e técnicas para o webpack usar mais
de um arquivo de configuração, eu prefiro seguir a documentação do
webpack usando o parâmetro –config.
"scripts": {
"build": "webpack --config prod.config.js"
}

• https://generatewebpackconfig.netlify.com/

• https://webpack.jakoblind.no/
TRABALHANDO COM ESTILOS
Nesta parte do livro, você deve aprender sobre tudo relacionado a
estilos, incluindo detalhes de carregamento, recarga da página quando
estillos forem alterados, separação de css do código da aplicação e
eliminar css não utilizado.

CARREGANDO ESTILOS
Este capítulo foca apenas em como carregar estios na sua página, durante
o processo de desenvolvimento. É preciso saber com antecedência que, o
webpack suporta uma grande variedade de tecnologias relacionadas com
estilos, como less, sass, postcss… Para cada uma dessas tecnologias, será
preciso um loader diferente ou uma técnica diferente. No momento,
vamos focar no carregamento de CSS por ser o mais comum e mais
usado por desenvolvedores.
O carregamento de CSS é feito por dois loaders: css-loader e style-loader.
O primeiro é usado para lidar com comandos @import e url() enquanto o
segundo é usado para lidar com CSS inline em atributos style. Além
disso, o style-loader também implementa o Hot Module Reload,
atualizando a página cada vez que os estilos forem modificados.
É possível fazer uma carga crua de CSS usando file-loader ou url-loader,
mas eles são usados para carregamento de assets.

INSTALANDO OS LOADERS
npm install css-loader style-loader –save-dev

CONFIGURANDO WEBPACK.CONFIG.JS
module: {
rules: [
{test: /\.css$/, use: ['style-loader', 'css-loader']}
],
},

Vale lembrar que os carregadores são usados em sequência da direita


para a esquerda e uns sobre o resultado dos outros. Como executar duas
funções aninhadas styleLoader(cssLoader(codigo));.
USANDO CSS
Para utilizar o css, basta usar o import com um arquivo css em qualquer
arquivo JavaScript. No caso de CSS globais, você pode realizar o import
dieretamente sobre o arquivo principal de sua aplicação.

OUTRAS DICAS IMPORTANTES


Um trabalho bem comum é utilizar bibliotecas de terceiros com CSS
externo, como bootstrap ou sweetalert. Nesses casos, você só precisa
instalar as bibliotecas via npm e depois importar o CSS diretamente da
pasta node_modules. Em casos assim, bastar realizar um import como
mostrado abaixo:
@import “~bootstrap/less/bootstrap”;
Também existe uma opção chamada bootstrap-loader que realizar a carga
de elementos instalados do bootstrap.
https://www.npmjs.com/package/bootstrap-loader

SEPARANDO ESTILOS CSS DO CÓDIGO JAVASCRIPT


Por padrão, todo o css carregado pelos loaders são adicionados de forma
inline ou dentro de uma tag style dentro da página de sua aplicação.
Como o código CSS não é minificado nem carregado dentro de um
arquivo CSS separado, você corre risco de obter um FOUC, expor seu
código CSS ou ter um maior tempo de carga por causa dos espaços e
quebras de linha dentro do seu código CSS.
FOUC: Flash of Unstyled Content, é o que acontece quando carregamos o
CSS dentro da página HTML. O conteúdo HTML sempre é carregado
primeiro, só depois o CSS é renderizado no conteúdo. Entre o
carregamento do conteúdo e a renderização do CSS existe um intervalo
de tempo. Dependendo do seu código, o usuário pode visualizar, mesmo
que por alguns segundos, o conteúdo HTML sem CSS. Esse flash de
conteúdo HTML é percebido como algo estranho e indesejado, além disso
não passa um ar de profissionalismo para o seu trabalho.
Para separar e compactar seu conteúdo CSS, vai ser preciso utilizar um
plugin chamado mini-css-estract-plugin. Além de coletar o CSS do seu
código, ele também possui a capacidade de juntar vários arquivos CSS
num mesmo arquivo e adicioná-lo na página como um arquivo separado.
Isso evita o FOUC mencionado acima.
Vale notar que o mini-css-extract-plugin não implementa o Hot Module
Reloader, tendo seu uso aconselhado apenas para produção e não
desenvolvimento.

INSTALANDO PLUGIN
npm install mini-css-extract-plugin –save-dev

CONFIGURANDO WEBAPACK.CONFIG.JS
module: {
rules: [
{test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader']}
],
},

plugins: [
new HtmlWebPackPlugin({
title: 'my webpack html',
}),
miniCssPlugin,
],

Note que removemos o loader style-loader, fizemos isso porque utilizar o


style-loader com o plugin produz um erro. Dessa forma, foi melhor
remover o style para que tudo funcione corretamente.
Também é possível perceber que o CSS gerado é a união de todos os
arquivos CSS usados no ponto de entrada e que eles não estão
minificados. Para compactar o código CSS gerado vai ser necessário
utilizar dois outros plugins: terser-webpack-plugin e optimize-css-assets-
webpack-plugin.
A ideia é sobrescrever o minimizer padrão do webpack, permitindo que,
em modo de produção, seu arquivo CSS seja minificado e em tempo de
desenvolvimento ele continue funcionando como antes.

INSTALANDO OS PLUGINS
npm install optimize-css-assets-webpack-plugin terser-webpack-plugin –save-dev

CONFIGURANDO O WEBPACK.CONFIG.JS
optimization: {
minimizer: [ new TerserJsPlugin({}), new OptimizeCssAssetsPlugin({}) ],
},

Feito isso, quando você compilar seu código em modo de produção, o


webpack vai minificar o código CSS e incluí-lo em seu na página HTML.

ELIMINANDO CSS NÃO USADO


Quem trabalha com bibliotecas de terceiros, como bootstrap, sabe que a
aplicação cresce consideravelmente para usar apenas algumas poucas
partes do código externo. Sendo assim, quem deseja uma aplicação
pequena, compacta e com poucas dependências, acaba sacrificando uma
boa parte do trabalho podando códigos desnecessários ou criando
pequenas bibliotecas com códigos de terceiros.
Para facilitar esse trabalho existe o plugin purify-css, que para o
webpack está encapsulado como purifycss-webpack. Ele simplesmente
passeia pelo código da aplicação separando trechos de código que estão
sendo usados dos inúteis, gerando um código CSS mais compacto e
aplicações mais leves. Note que isso está focado no CSS, não espere que
outras tecnologias sejam compactadas com essa técnica.
Para usar o purifycss-webpack é necessário indicar o caminho para os
arquivos de sua aplicação. Conseguimos realizar essa operação usando o
componente glob para gerar o caminho para os arquivos de nossa
aplicação.

INSTALANDO O PLUGIN
npm install glob purifycss-webpack purify-css –save-dev

CONFIGURANDO O ARQUIVO WEBPACK.CONFIG.JS


Importe os módulos necessários para configurar o aruqivo.
const PurifyCssPlugin = require('purifycss-webpack');

Para facilitar o trabalho, criamos uma variável contendo o caminho


absoluto para a pasta da aplicação em ./src.
const PATH_TO_SRC = path.join(__dirname, 'src');

Depois adicionamos o plugin depois do MiniCssExtractPlugin.


plugins: [
new HtmlWebPackPlugin({
title: 'my webpack html',
}),
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new PurifyCssPlugin({
paths: glob.sync(`${PATHS}/**/*.js`, {nodir: true})
}),
],

Agora, ao executar o build da aplicação você vai notar que o arquivo


main.css vai ficar com o tamanho bem menor e apenas com os códigos
CSS utilizados pela aplicação.
TRABALHANDO COM ASSETS
Assets geralmente estão relacionados com arquivos externos, ou de
terceiros. Nesse capítulo o autor mostra como trabalhar com arquivos.
Definir melhor o que é um asset.

DEFININDO LOADERS
Como primeira parte do trabalho, precisamos entender como definir um
loader no webpack, uma vez que os loaders são usados para carregar
arquivos, saber sua definição e opções é a melhor forma de começar a
trabalhar com arquivos externos ao código.
Apesar do webpack ter vários caminhos e usos para loaders, vou ficar
com as opções mais básicas. Não é interesse mostrar algo complexo por
motivos de manutenabilidade de código. Mesmo algumas opções mais
simples como loaders inline não é recomendado pelo mesmo motivo já
citado. Se você quer um código mais leve, consistente e fácil de manter,
procure seguir o padrão da simplicidade de uso.

ANATOMIA DE UM LOADER
Um loader é um objeto adicionado nos atributos module.rules de nosso
webpack.config.js. Ele possui as seguintes opções comuns:
test: Geralmente uma expressão regular para testar se o arquivo
encontrado é o tipo de arquivo que será usado pelo loader. É muito
comum encontrar expressões regulares como /\.css$/. Essa expressão
regular verifica se o arquivo encontrado tem a extensão .css. Tendo essa
extensão, o loader passa a ser usado nele.
include: Pode ser uma String ou expressão regular indicando uma ou
mais pastas contendo arquivos que serão usados pelo loader.
exclude: Uma String ou expressão regular indicando pastas com conteúdo
que deve ser ignorado pelo loader.
use: Uma String ou Array de Strings contendo o loader ou tipo de loader
usado na configuração. Existe uma variedade deles e os mais comuns são
file-loader, raw-loader e url-loader.
options: Um objeto de parâmetros passados para o loader.
O código abaixo mostra como um loader é usado na prática.
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},

Vale lembrar que os loaders são como cadeias de execução, onde o


resultado do loader mais a baixo na lista de rules é usado pelo loader
mais acima na lista. É como se você estivesse usando funções no estilo
babelLoader( fileLoader(codigo) ).

CARREGANDO IMAGENS
Existem dois métodos de carregar imagens, um usando o url-loader e
outro usando o file-loader.

USANDO URL-LOADER
É usado para adicionar imagens diretamente no código. As imagens são
convertidas em textos base64 e adicionadas diretamente no código HTML
ou JavaScript. Isso faz seus pacotes de distribuição maiores e diminui a
quantidade de requisições do navegador por imagens no servidor. Ele é
uma alternativa viável e rápida para o modo de desenvolvimento, mas
não é recomendado para a distribuição em produção.
Primeiramente você vai precisar instalar o loader como dependência de
desenvolvimento.
npm install url-loader –save-dev
Em seguida, configurar o url-loader para carregar arquivos no atributo
modules.rules do webpack.config.js. Caso você esteja usando a
compactação de CSS mostrados nos capítulos anteriores, aconselhamos
adicionar o loader no fim da lista.
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.png$/,
use: 'url-loader'
},
],
},

Depois que você iniciar a aplicação usando npm start ou npm run dev,
vai perceber que todo o código CSS com uma imagem em url() vai ser
transformada em imagens no formato texto base64.
#image-class {
background-image: url("../assets/img/tomato.png");
background-repeat: no-repeat;
background-position: center;
height: 100px;
}

É transformado em…
#image-class {
background-image: url(data:image/png;base64,iVBORw0KGgoAA...kJggg==);
background-repeat: no-repeat;
background-position: center;
height: 100px;
}

Quando uma imagem é usada no código JavaScript, no padrão import, o


url-loader também exporta a imagem como texto base64, mas
diretamente o pacote de deistribuição gerado.
import imagem from '../assets/img/lemon.png';

document.write(`<img src="${imagem}" />`);

É transformado em…
/***/ "./assets/img/lemon.png":
/*!******************************!*\
!*** ./assets/img/lemon.png ***!
\******************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("module.exports = \"data:image/png;base64,iVBORw...Jggg==\"\n\n//#
sourceURL=webpack:///./assets/img/lemon.png?");

/***/ }),
Vale notar que o url-loader possui a opção limit, que por padrão é
8192kb. Essa opção determina que apenas imagens abaixo do limite
podem ser convertidas em texto base64. Se você precisar de imagens
maiores, é necessário aumentar o limite como mostrado abaixo.
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.png$/,
use: 'url-loader',
options: {
limit: 100000
}
},
],
},

USANDO FILE-LOADER
Diferente do url-loader, o file-loader exporta as imagens como arquivos
separados na pasta dist, tornado o carregamento do seu pacote de
distribuição mais leve e aumentando as requisições do navegador. Em
modo de produção isso pode ser desejado quando você possui poucas
imagens e fica a cargo do navegador decidir quando renderizá-las para o
usuário.
Outro ponto forte do file-loader é a capacidade de carregar diferentes
arquivos, como imagens e fontes. Vamos usar o file-loader para carregar
imagens em arquivos separados.
Primeiramente instale o file-loader como dependência de
desenvolvimento.
npm install file-loader –save-dev

Depois configure o file-loader no webpack.config.js. Se você estiver


usando o url-loader, vai ser necessário remover a configuração dele para
poder usar o file-loader, caso contrário receberemos uma mensagem de
erro a a aplicação não vai funcionar.
{
test: /\.png$/,
use: {
loader: 'file-loader',
options: {
name: "[path][name].[hash].[ext]"
},
},
},

Como você já deve estar imaginando, a opção name: "[path][name].


[hash].[ext]" recria a estrutura de pastas de suas imagens na pasta dist,
criando os arquivos com o mesmo nome e um hash de verificação para
quando suas imagens forem alteradas. Esse hash é importante porque
permite usar o cache do navegador de internet, diminuindo a quantidade
de requisições realizadas pela aplicação para baixar a mesma imagem.
Quem realiza desenvolvimento de Jogos ou prefere juntar várias imagens
num único arquivo, pode usar o svg-sprite-loader para carregar sprites
em arquivos svg, png ou jpg. Preciso procurar mais informações sobre o
assunto.
https://www.npmjs.com/package/svg-sprite-loader

CARREGANDO FONTES
Carregar fontes segue o mesmo processo de carregar imagens usando file-
loader. Também é possível carregar fontes usando url-loader, mas isso
exige algumas modificações nas opções do loader. Se quiser algo prático
e funcional, recomendo usar o file-loader para carregar fontes.

CARREGANDO JAVASCRIPTS
Aqui não se trata de realmente carregar códigos JavaScript externos, mas
de transformar seu código JavaScript em algo compatível com
navegadores mais antigos. Esse processo de transformação é conhecido
como transpiling ou tradução.
Os tradutores mais populares para JavaScript conhecidos hoje são babel e
TypeScript, que convertem o código JavaScript mais recente para ECMA
Script 2015, que é compatível com navegadores mais antigos. No capítulo
atual vamos nos ater ao babel por ser mais popular que o TypeScript.
Primeiro instalamos o babel-loader e as bibliotecas básicas do babel.
npm install babel-loader @babel/core –save-dev
Em seguida configurar o webpack.config.js para utilizar o loader do
babel.
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},

Depois disso, é preciso criar o arquivo .babelrc na pasta raiz do projeto.


Esse arquivo serve para informar o babel que o nosso código ES2015 ou
superior deve ser convertido para ES5 quando compilado em modo de
produção. O arquivo .babelrc deve ter seu conteúdo como mostrado
abaixo:
{
"presets": ["@babel/preset-env"]
}

Para mais informações sobre o babel, visite o site https://babeljs.io


BUILDING
Esta parte do livro fala sobre como trabalhar com mapas de fontes
(source-maps), como quebrar os pacotes em arquivos menores por vários
caminhos diverentes. Descubra como melhorar seus resultados aqui.

TRABALHANDO COM SOURCE-MAPS


Como códigos minificados passaram a ser usados como padrão,
melhorando o carregamento e execução de códigos HTML, CSS e
JavaScript, os mesmos código minificados também dificultaram a
depuração de uma aplicação, uma vez que códigos assim trabalhados são
difíceis de compreender. Os source-maps são uma forma de indicar ao
navegador como exibir o código minificado de modo compreensível ao
desenvolvedor, permitindo depurar o código no modo original, como se
ele não estivesse minificado. Eles fazem isso mostrando ao navegador
como “desminificar” os códigos, facilitando o desenvolvimento.
Por padrão, em modo de desenvolvimento, os source-maps são expostos
nas ferramentas de desenvolvedores de cada navegador, permitindo o uso
dessas ferramentas para desenvolvimento e depuração. Em modo de
produção, os source-maps não são gerados, fornecendo mais segurança,
mostrando apenas o código minificado e evitando que um outro
desenvolvedor possa compreender o código que você escreveu.
Para fins de velocidade, seja em desenvolvimento ou produção, o
webpack permite que você use algumas opções diferentes de source-maps.
No link abaixo, segue uma tabela com as opções de source-maps que
você pode usar em suas aplicações.
https://webpack.js.org/configuration/devtool
Eu sei que existem outras formas de configurar e controlar a criação de
source-maps, mas no momento, é melhor compreender e saber utilizar as
opções padrões antes de se aprofundar por situações mais complexas.
Além disso, as opções padrões já suprem quase todas as possibilidades de
uso tanto no desenvolvimento quanto em produção.
A documentação do Webpack também recomenda que você escolha uma
opção: ou você usa as opções padrões do source-map, ou utiliza algum
plugin para controlar isso diretamente, mas nunca utilize as duas opções
ao mesmo tempo, caso contrário você vai desencadear muitos problemas
inesperados.

QUANDO USAR CADA OPÇÃO


O uso de um tipo de source-map vai depender de suas necessidades,
todas avaliadas sobre três critérios, qualidade, velocidade e segurança.
Quanto mais qualidade, mais informações sobre o código original você
terá, como números de linha, números de colunas, uso de breakpoints
das ferramentas de desenvolvimento do navegador corretamente e etc.
Quanto mais velocidade na criação do pacote de distribuição, seja em
desenvolvimento ou produção, menor a qualidade e fidelidade ao código
original, comprometendo a depuração.
Por permitir a compreensão do código original, sem minificação, usar
source-maps de modo explícito em produção é o mesmo que explicar o
seu código para qualquer usuário, seja um usuário avançado ou não.
Sendo assim, a segurança consiste no nível de visibilidade do código
original em desenvolvimento e produção.

SOURCE-MAPS PARA DESENVOLVIMENTO


A melhor opção para desenvolvimento é o eval-source-map, permitindo
uma ótima correlação entre o código gerado e o código original. É um
pouco mais lento no início, mas ele não recria totalmente o source-map
a cada modificação, ele apenas modifica o que for necessário no source-
map, aumentando a velocidade com o tempo de desenvolvimento.

SOURCE-MAPS PARA PRODUÇÃO


O ideal é que não exista source-map em produção, mas se você precisar
depurar comportamento inesperado, principalmente os intermitentes, que
hora acontecem e hora não acontecem, você pode usar hidden-source-
map. Ele não expõe o código original para os usuários, mas em caso de
erros ou breakpoints, como debugger, consegue expor mensagens com
informações adaptadas para o código original. Assim você conseguirá
depurar sua aplicação em produção sem perder a segurança do código.
Se você precisar de mais qualidade de source-maps no ambiente de
produção, recomendo configurar seu servidor web para bloquear o acesso
à source-maps para usuários comuns. Ainda não sei como fazer isso e a
documentação do webpack não fala sobre o assunto.
O atributo usado para configurar os source-maps é o devtool do
webpack.
devtool: '#eval-source-map',

LINKS ÚTEIS PARA MAIORES INFORMAÇÕES


• Para entender como funciona o source-map em detalhes:
https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/
• Comparação entre source-map e código original:
https://sokra.github.io/source-map-visualization/
• Chrome DevTools: https://developers.google.com/web/tools/chrome-
devtools/?
utm_source=dcc&utm_medium=redirect&utm_campaign=2016q3
• FireFox DevTools:
https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Us
e_a_source_map
• IE/Edge DevTools:
https://docs.microsoft.com/pt-br/microsoft-edge/devtools-guide/debug
ger#source-maps
• Safari DevTools: https://support.apple.com/pt-br/guide/safari/use-the-
safari-develop-menu-sfri20948/mac
• Sobre tipos de source-maps:
http://cheng.logdown.com/posts/2016/03/25/679045

BUNDLE SPLITING
Consiste em dividir o código javaScript gerado em módulos. Assim,
apenas o módulo modificado é baixado pelo navegador quando o usuário
utilizar sua aplicação. É um melhor uso do cache do navegador.
Por exemplo, você pode ter uma aplicação contendo 1ookb num único
arquivo JavaScript, somando o seu código com o código de terceiro, um
JQuery por exempolo. Separar códigos significa gerar um arquivo com
seu código, contendo 10kb, e outro contendo o código do JQuery,
contendo 90kb.
Como apenas o seu código é alterado constantemente, o carregamento da
aplicação é otimizado pelo cache do navegador, que identifica apenas os
10kb de seu código como necessário para carregamento constante e não
os 100kb.
A separação de arquivos, na versão 04 do webpack, ocorre naturalmente
se uso de plugins. Para isso, basta configurar o atributo
optimization.splitChunks gerando arquivos numerados estilo 1.main.
[hash].js.
{
optimization: {
splitChunks: {
chunks: "initial",
},
},
},

Você também pode modificar a nomeação dos módulos, usando a


configuração abaixo:
{
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "initial",
},
},
},
},
},

De qualquer forma é uma boa ideia ler mais sobre o assunto na


documentação do webpack e fazer testes.
https://webpack.js.org/configuration/optimization
Os plugins para split chunks foram removidos da versão 4 do webpack.
https://gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693
Blog oficial do webpack falando sobre split e merge chunks:
https://medium.com/webpack/webpack-http-2-7083ec3f3ce6
Documentação mais detalhada sobre splitChunks do webpack:
https://webpack.js.org/plugins/split-chunks-plugin/
Apesar de ser um bom recurso para otimizar o carregamento do código,
ele ainda não faz o que eu gostaria. Em toda aplicação existem módulos
genéricos compartilhados e módulos em constante alteração. Esses
módulos genéricos seriam melhor aproveitados se gerados em arquivos
separados, principalmente em aplicações multipage.

CODE SPLITING
Consiste em marcar o código com imports especiais para carregamento
tardio de modulos ou funcções de sua aplicação. Assim, a aplicação pode
ser carregada de modo mais rápido, pois fica com um tamanho
considerávelmente menor e o restante do código é carregado apenas
quando for necessário.
Para isso vai ser necessário instalar um plugin especial do babel,
chamado @babel/plugin-syntax-dynamic-import.

npm install @babel/plugin-syntax-dynamic-import –save-dev

Depois disso, o comando import passa a funcionar como uma promise,


permitindo importar códigos ou módulos dinamicamente para a aplicação.
Então eu crio uma função num arquivo separado chamado pad.js.
export default function (num) {
if (num < 10) {
return '0' + num;
}

return ''+num;
}

No arquivo .babelrc, indicamos ao babel que o plugin vai ser utilizado.


{
"plugins": ["@babel/plugin-syntax-dynamic-import"],
...
}
E em seguida modifico a aplicação principal para usar o pad.js no clique
de um botão, realizando o carregamento do arquivo apenas quando o
usuário executar o evento de click.
function showHello (event) {
import('./lib/hello')
.then(modulo => alert(modulo.default))
.catch(error => console.log(error));
};

let button = jQuery('<button>', {type: 'button'})


.text('Clique em mim')
.click(showHello);

document.getElementsByTagName('body')[0].appendChild(button[0]);

Se você observar as requisições de rede, vai perceber que no momento


do clique no botão, um arquivo JavaScript é carregado e executado. Esse
é o lazyload do código quebrado em pedaços.
Use isso com moderação, não sei como essa técnica se comportará com
uma grande quantidade de carregamentos ou carregando arquivos
maiores. Tome cuidado.

• Entendendo o que é PRPL do Google: https://developers.google.com/


web/fundamentals/performance/prpl-pattern/
• Como desabilitar o code spliting no webpack: https://medium.com/
@glennreyes/how-to-disable-code-splitting-in-webpack-1c0b1754a3c5

TIDYNG UP (ARRUMANDO A CASA)


Consiste em limpar a pasta dist em cada build realizado e adicionar um
código de versão no build atual. Esse código de versão é modificado a
cada build.

LIMPANDO A PASTA DIST


Para isso vai precisar do plugin clean-webpack-plugin.
npm install clean-webpack-plugin –save-dev
Depois configurar o plugin no webpack.config.js.
const CleanWebpackPlugin = require('clean-webpack-plugin');

plugins: [
...
new CleanWebpackPlugin()
],

ANEXANDO UM CÓDIGO DE COMPILAÇÃO


Essa parte do livro eu achei particularmente inútil. Consiste em adicionar
um comentário no topo de cada arquivo com o código de versão do Git.
Ele usa o BannerPlugineGitRevisionPlugin.

INSTALANDO O PLUGIN
npm install git-revision-webpack-plugin –save-dev

CONFIGURANDO O WEBPACK.CONFIG.JS
const webpack = require("webpack");
const GitRevisionPlugin = require("git-revision-webpack-plugin");
...
plugins: [
new webpack.BannerPlugin({
banner: new GitRevisionPlugin().version(),
}),
]

Depois disso, quando realizar o build, o webpack vai adicionar /*!


0b5bb05 */ ou /*! v1.7.0-9-g5f82fe8 */ no início de cada arquivo.
Além disso, também é possível passar variáveis de ambiente para dentro
do código JavaScript de sua aplicação usando webpack.DefinePlugin, mas
isso será visto adiante no livro.

COPIANDO ARQUIVOS DO WEBAPACK PARA OUTRO LUGAR


Um recurso interessante é a operação de cópia de arquivos do webpack
para outro lugar do sistema de arquivos. Isso pode ser particularmente
útil para integrar uma aplicação multipage com um servidor web ou
framework como CakePHP.
• Solução dentro do webpack: https://www.npmjs.com/package/copy-
webpack-plugin
• Solução multiplataforma fora do webpack:
https://www.npmjs.com/package/cpy-cli
OTIMIZAÇÃO (OPTIMIZING)
Aspectos como minificação, variáveis de ambiente, hash em nomes de
arquivos, webpack manifest, análise de pacotes de distribuição gerados e
outras técnicas de performance, tanto no processo de criação dos pacotes
de distribuição quanto no código gerado.

MINIFICAÇÃO
Na versão 4 do webpack, o modo de produção já minifica o código
JavaScript por padrão, usando o plugin Terser, uma versão mais
avançada do UglifyJs que suporta o ECMA Script 2015 ou superior.
Como o webpack 4 já minifica códigos por padrão, o capítulo serve
apenas de orientação sobre como personalizar o processo do minificação
de códigos.
O que o processo de minificação faz é converter seu código JavaScript
num formato menor, procurando não perder a lógica do código já
escrito. Geralmente é um processo bem eficiente, mas existem alguns
casos que merecem atenção, como a substituição de variáveis. Em casos
onde uma condição for sempre falsa, ou sempre verdadeira, seja por erro
de lógica ou intencional, o processo de minificação substitui a expressão
condicional pelo resultado da expressão. Observe o código abaixo:
const FOO = ‘foo’;

if (FOO === ‘bar’) {


console.log(‘bar’);
}
if (FOO === ‘foo’) {
console.log(‘foo’);
}

Ao minificar o código acima, ele será transformado no código abaixo:


if (false) {
console.log(‘bar’);
}

if (true) {
console.log(‘bar’);
}

Em seguida, o código que nunca será executado, também chamado de


código morte, é removido da aplicação, diminuindo ainda mais o
tamanho dos pacotes gerados. No final, o código gerado pela minificação
será como mostrado abaixo.
console.log(‘bar’);

Note que as condições para esse resultado é usar uma constante, onde o
valor não se modifica durante a execução do código, ou uma expressão
de resultado único, como if (‘foo’ === ‘bar’).
Isso é interessante para conseguir um comportamento no modo de
desenvolvimento, como o uso de comandos console.log ou debugger,
diferente do modo de produção, sem os comandos mencionados. De
qualquer forma, esse processo de seleção de comandos para
desenvolvimento e produção será mencionado mais a frente.

INSTALANDO O TERSER PLUGIN


npm install terser-webpack-plugin –save-dev

CONFIGURANDO O WEBPACK.CONFIG.JS
const TerserPlugin = require("terser-webpack-plugin");

...
optimization: {
minimize: true,
minimizer: [new TerserPlugin({ sourceMap: true })],
},
...

Existem outros mecanismos para minificação como os plugins do próprio


babel, babel-minify-webpack-plugin e babel-preset-minify.
webpack-closure-compiler e o closure-webpack-plugin
butternut-webpack-plugin de Rich Harri’s, ainda em fase experimental.

MINIFICANDO HTML
Essa dica vai para quem trabalha com html-loader. Você pode processar
o código carregado usando posthtml com o posthtml-loader ou usar o
posthtml-minifier para minificar o código HTML.

MELHORANDO A MINIFICAÇÃO DO CSS


Esse processo me parece inútil, pois a configuração anterior já remove
comentários e códigos não utilizados de CSS.
No processo que mencionamos anteriormente, usando o
MiniCssExtractPlugin, apenas comprimimos os códigos CSS, mantendo
comentários e códigos desnecessários. Para evitar isso, basta utilizar o
plugin anterior com o optimize-css-assets-webpack-plugin e cssnano.

INSTALANDO…
npm install optimize-css-assets-webpack-plugin cssnano –save-dev

CONFIGURANDO
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const cssnano = require("cssnano");

plugins: [
new OptimizeCSSAssetsPlugin({
cssProcessor: cssnano,
cssProcessorOptions: options,
canPrint: false,
}),
],

MINIFICANDO IMAGENS
Imagens podem ser comprimidas usando os plugins abaixo:
• img-loader, imagemin-webpack, imagemin-webpack-plugin

TREE SHAKE (HOISTING OPTIMIZATION)


Acontece quando estamos usando o escopo do JavaScript num módulo e
exportando classes e funções no final dele. O que a minificação faz é
separar o código utilizado do não usado, é uma boa forma de concentrar
códigos em módulos e gerar aplicações menores.
Suponha que você tenha o módulo modulo.js com o código abaixo:
const bake = () => {
console.log('bake');
};

const shake = () => {


console.log('shake');
};

export { bake, shake };

E na aplicação esteja importando apenas a função bake, como mostrado


abaixo:
import { bake } from './modulo';
bake();

O pacote de distribuição gerado pelo build configurado com o Terser vai


conter o código abaixo:
console.log(‘bake’);

Isso é muito útil para modulos utilitários, ou helpers, onde você adiciona
apenas o código que está sendo utilizado no pacote de distribuição. Antes
eu criava um módulo separado para cada função helper, mas hoje
concentro os helpers em arquivos com temas comuns, facillitando a
localização e manutenção deles.

VARIÁVEIS DE AMBIENTE
Ferramenta útil para executar parte do código em desenvolvimento e
parte em produção, até mesmo modificar o comportamento com
impressão de logs e debugger.
Lembre-se que, dentro do processo de minificação do código, ajuda a
melhorar os comandos, transformando o código abaixo…
if (process.env.NODE_ENV == ‘development’) {
console.log(‘development’);
}
… no código abaixo ...
console.log(‘development’)
caso a condição seja verdadeira. Caso a condição seja falsa, o console.log
simplesmente é removido do código.
Adicionar variáveis de ambiente é muito fácil usando DefinePlugin no
webpack.config.js para que a variável fique disponível no código da
aplicação.
const webpack = require('webpack');

Plugins: [
new webpack.DefinePlugin({HELLO: JSON.stringify(‘hello world’)})
],
O comando JSON.stringify é necessário para identificar o valor
corretamente na variável de ambiente. Se você não fizer isso, podem
ocorrer erros de interpretação dos valores. Depois é só usar ela no seu
código.
export default (text = HELLO) => {
const element = document.createElement("div");

...
};

Nessas anotações do livro, estão faltando as partes de Análise do pacote


de distribuição gerado e a performance na criação do pacote.
OUTPUT (SAÍDAS)
Nessa parte do livro, cobrimos técnicas de voltadas para os arquivos
gerados no seu pacote de distribuição. Veja como gerenciar uma
configuração multipage e como identificar a renderização no lado
servidor.

TARGETS (DESTINOS)
Pelo que pude perceber, targets são indicadores de tecnologias alvo do
programador, permitindo ao webpack realizar otimizações específicas para
a tecnologia.
Quando se trata do desenvolvimento JavaScript, podemos criar aplicações
para Web, para mobile e desktops usando diferentes tecnologias, como
nodejs, electron e outros. Assim, informar seu objetivo ao webpack
permite que o mesmo prepare seu pacote de distribuição para ser
executado em cima da tecnologia alvo.
Por padrão o webpack usa o target web, mas ele vem com opções para
outras otimizações, como as relacionadas abaixo:
async-node: NodeJs carregando módulos de forma assíncrona.
electron-main: Prepara o código para o processo main do Electron.
electron-renderer: Prepara o código para o processo de renderização do
Electron, usando JsonpTemplatePlugin e FunctionModulePlugin para
ambientes de navegadores ou NodeTargetPlugin e ExternalsPlugin para
CommonJS e módulo nativos do Electron.
node: Prepara o código no estilo NodeJs, usando require para carregar
módulos JavaScript.
web: É o padrão, preparando o código para rodar em navegadores de
internet.
webworker: Prepara o código para trabalhar com WebWorkers.
Como o foco deste trabalho está no desenvolvimento Web, minha
principal atividade, nada deve ser alterado nas configurações do
webpack.config.js. Esse capítulo é meramente informativo. De qualquer
forma, é uma boa opção voltar aqui para estudar melhor e começar a
criar aplicativos com Electron novamente.
https://webpack.js.org/configuration/target
https://github.com/webpack/webpack/blob/master/lib/
WebpackOptionsApply.js#L148-L183
https://medium.com/code-oil/webpack-javascript-bundling-for-both-front-
end-and-back-end-b95f1b429810
https://jlongster.com/Backend-Apps-with-Webpack--Part-I

MULTIPAGES
Lida com a criação de várias páginas no pacote de distribuição. No lugar
de um ponto de entrada, temos vários pontos de entrada; e para cada
ponto de entrada temos uma página de template gerada com
HtmlWebpackPlugin. Dessa forma, podemos usar o webpack-dev-server
para trabalhar com várias páginas diferentes.
Isso ainda não resolve meu problema. Eu preciso de algo que interaja
com o servidor web que estou usando para construir aplicações, no meu
caso, o apache ou nginx.
A primeira forma de solução ideal, seria gerar o JavaScript para uma
determinada página, já dentro da pasta js da minha aplicação apache,
então o webpack-dev-server abriria o navegador na página de login,
permitindo a navegação até a página em desenvolvimento. A partir daí,
quando alterar o código JavaScript, o webpack deveria gerar o novo
JavaScript na pasta js da minha aplicação e recarregar a página atual.
Essa seria a solução ideal para meu workflow.