Webpack Notas Estudo
Webpack Notas Estudo
Webpack Notas Estudo
NOTAS DE ESTUDO
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.
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
PACKAGE.JSON
Antes de fazer qualquer coisa com webpack, é preciso:
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’);
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>
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
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.
• 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']}
],
},
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,
],
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({}) ],
},
INSTALANDO O PLUGIN
npm install glob purifycss-webpack purify-css –save-dev
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]'
}
}
]
},
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;
}
É 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
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',
},
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",
},
},
},
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.
return ''+num;
}
document.getElementsByTagName('body')[0].appendChild(button[0]);
plugins: [
...
new CleanWebpackPlugin()
],
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(),
}),
]
…
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 (true) {
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.
CONFIGURANDO O WEBPACK.CONFIG.JS
const TerserPlugin = require("terser-webpack-plugin");
...
optimization: {
minimize: true,
minimizer: [new TerserPlugin({ sourceMap: true })],
},
...
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.
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
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");
...
};
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.