Configuração Inicial do Webpack
Instalação e Preparação do Projeto
Para iniciar um projeto com Webpack, primeiro inicialize o gerenciador de pacotes:
npm init -y
Isso gerará o arquivo package.json. Em seguida, instale o Webpack e o CLI como dependências de desenvolvimento:
npm install webpack webpack-cli --save-dev
A instalação global não é recomendada, pois diferentes projetos podem exigir versões distintas.
Estrutura Básica do Projeto
Crie as pastas src e o arquivo index.html na raiz. Dentro de src, crie app.js e greetings.js:
app.js
import { greet } from './greetings';
greet();
greetings.js
export function greet() {
console.log('Bem-vindo ao projeto');
}
Arquivo de Configuração
Crie webpack.config.js na raiz do projeto:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
Adicione o script de build no package.json:
"scripts": {
"build": "webpack"
}
Execute npm run build para gerar o arquivo dist/bundle.js.
No index.html, referencie o bundle gerado:
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Meu Projeto</title>
</head>
<body>
<script src="./dist/bundle.js"></script>
</body>
</html>
Separação de Configurações por Ambiente
Parra ambientes distintos, instale o webpack-merge:
npm install webpack-merge --save-dev
Crie três arquivos de configuração:
webpack.base.js (configuração compartilhada):
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
webpack.dev.js (desenvolvimento):
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
module.exports = merge(baseConfig, {
mode: 'development'
});
webpack.prod.js (produção):
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
module.exports = merge(baseConfig, {
mode: 'production'
});
Servidor de Desenvolvimento Local
O webpack-dev-server mantém os arquivos compilados em memória, proporcionando maior velocidade:
npm install webpack-dev-server --save-dev
Configure os scripts no package.json:
"scripts": {
"build": "webpack --config webpack.prod.js",
"start": "webpack serve --config webpack.dev.js --open"
}
Adicione a configuração do servidor em webpack.dev.js:
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
module.exports = merge(baseConfig, {
mode: 'development',
devServer: {
static: './dist',
port: 3000,
open: true,
proxy: {
'/servico': {
target: 'http://localhost:4000',
changeOrigin: true,
pathRewrite: { '^/servico': '' }
}
}
}
});
Geração Automática do HTML
Para injetar automaticamente o bundle no HTML, utilize o HtmlWebpackPlugin:
npm install html-webpack-plugin --save-dev
Configure em webpack.base.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
filename: 'index.html'
})
]
};
Processamento de Estilos
Instale os loaders necessários para CSS, Less e Sass:
npm install css-loader style-loader less less-loader sass sass-loader --save-dev
Adicione as regras em webpack.base.js:
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
Compatibilidade Cross-Browser com PostCSS
npm install postcss postcss-loader postcss-preset-env --save-dev
Crie postcss.config.js:
module.exports = {
plugins: [
require('postcss-preset-env')
]
};
Inclua o postcss-loader na cadeia de processamento de CSS:
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
No package.json, defina os navegadores alvo:
"browserslist": [
"last 2 versions",
"> 1%"
]
Processamento de Imagens e Fontes
Desde o Webpack 5, recursos estáticos são tratados nativamente via módulos de asset. Para imagens com codificação Base64 condicional:
{
test: /\.(png|jpe?g|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 25 * 1024
}
},
generator: {
filename: 'midia/imagens/[name].[hash:6][ext]'
}
}
Imagens menores que 25KB serão convertidas para Data URL, reduzindo requisições HTTP. Para fontes:
{
test: /\.(woff2?|ttf|otf|eot)$/,
type: 'asset/resource',
generator: {
filename: 'midia/fontes/[hash:8][ext]'
}
}
Personalização dos Caminhos de Saída
Em webpack.prod.js, organize os recursos de produção:
const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
module.exports = merge(baseConfig, {
mode: 'production',
output: {
filename: 'js/[name].[contenthash:8].js',
path: path.resolve(__dirname, 'dist'),
clean: true
}
});
Configuração de Múltiplas Páginas
Para projetos com múltiplas páginas, configure múltiplos pontos de entrada:
entry: {
principal: './src/principal.js',
administrativo: './src/admin.js'
}
Configure o HtmlWebpackPlugin para cada página:
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
filename: 'index.html',
chunks: ['principal']
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'admin.html'),
filename: 'admin.html',
chunks: ['administrativo']
})
]
Ajuste o output para gerar arquivos com nomes dinâmicos:
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'dist'),
clean: true
}
Extração e Minificação de CSS
Para extrair CSS em arquivos separados em produção, utilize o MiniCssExtractPlugin:
npm install mini-css-extract-plugin --save-dev
Em webpack.prod.js, substitua style-loader por MiniCssExtractPlugin.loader:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/estilos.[contenthash:8].css'
})
]
});
Minificação do CSS
Instale o plugin de otimização:
npm install css-minimizer-webpack-plugin --save-dev
Adicione ao array de plugins em produção:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
plugins: [
new MiniCssExtractPlugin({
filename: 'css/estilos.[contenthash:8].css'
}),
new CssMinimizerPlugin()
]
Extração de Código Compartilhado
Utilize optimization.splitChunks para separar dependências de terceiros e módulos reutilizados:
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
bibliotecas: {
test: /[\\/]node_modules[\\/]/,
name: 'bibliotecas',
priority: 10,
minSize: 0,
minChunks: 1
},
compartilhados: {
name: 'compartilhados',
priority: 5,
minSize: 0,
minChunks: 2
}
}
}
}
Quando houver múltiplos pontos de entrada, referencie os chunks extraídos nos respectivos HTMLs:
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
filename: 'index.html',
chunks: ['principal', 'compartilhados', 'bibliotecas']
})
Carregamento Preguiçoso (Lazy Loading)
A importação dinâmica permite carregar módulos sob demanda:
setTimeout(() => {
import('./dados-dinamicos').then(modulo => {
console.log(modulo.default.informacao);
});
}, 2000);
O módulo dados-dinamicos.js será extraído automaticamente como um chunk separado durante o build.
Suporte a React e Vue
Configuração para React
npm install --save-dev @babel/preset-react
Adicione a regra de processamento de JavaScript:
{
test: /\.(js|jsx)$/,
use: ['babel-loader'],
exclude: /node_modules/
}
Configuração para Vue
npm install --save-dev vue-loader vue-template-compiler
{
test: /\.vue$/,
loader: 'vue-loader'
}
Otimização da Velocidade de Build
Cache do Babel
Habilite o cache do babel-loader para acelerar builds subsequentes:
{
test: /\.js$/,
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}],
exclude: /node_modules/
}
IgnorePlugin
Para bibliotecas como o Moment.js, evite carregar todos os idiomas desnecessários:
const webpack = require('webpack');
plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
})
]
Importe manualmente apenas o idioma necessário:
import moment from 'moment';
import 'moment/locale/pt-br';
noParse
Evite que o Webpack processe arquivos já minificados:
module: {
noParse: [/\.min\.js$/],
rules: [/* ... */]
}
Diferença fundamental: IgnorePlugin remove completamente a importação (exige inclusão manual), enquanto noParse mantém o módulo importado mas omite a etapa de parsing.
Processamento Paralelo com thread-loader
npm install thread-loader --save-dev
{
test: /\.js$/,
exclude: /node_modules/,
use: [
'thread-loader',
{
loader: 'babel-loader',
options: { cacheDirectory: true }
}
]
}
Para projetos pequenos, o overhead de criação de processos pode superar o benefício. Utilize apenas em projetos extensos com tempos de build elevados.
Hot Module Replacement (HMR)
No Webpack 5, o webpack-dev-server ativa o HMR por padrão. Para desabilitá-lo e usar recarregamento completo:
devServer: {
hot: false,
port: 3000,
open: true
}
DllPlugin para Dependências Estáveis
Para frameworks como React e Vue, que raramente mudam de versão, pré-compile as dependências:
Crie webpack.dll.js:
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
mode: 'production',
entry: {
vendor: ['react', 'react-dom']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dll'),
library: 'dll_[name]'
},
plugins: [
new DllPlugin({
name: 'dll_[name]',
path: path.join(__dirname, 'dll', '[name].manifest.json')
})
]
};
Execute webpack --config webpack.dll.js para gerar os arquivos pré-compilados.
Na configuração de desenvolvimento, referencie o manifesto:
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
plugins: [
new DllReferencePlugin({
manifest: require('./dll/vendor.manifest.json')
})
]
Otimização do Código de Produção
As estratégias fundamentais de otimização incluem:
- Codificação Base64 para recursos pequenos: reduz requisições HTTP convertendo imagens pequenas em Data URLs inline
- Hashing baseado em conteúdo: utilize
[contenthash]nos nomes dos arquivos de saída para garantir cache eficiente — o hash só muda quando o conteúdo é alterado - Importação dinâmica: carregue módulos sob demanda com
import() - Extração de código compartilhado: separe bibliotecas de terceiros e módulos reutilizados com
splitChunks - IgnorePlugin: elimine módulos desnecessários de bibliotecas como Moment.js
- Modo production: ativa automaticamente minificação, Tree-Shaking e remoção de código de depuração
No modo production, o Webpack aplica Tree-Shaking automaticamente. Considere este exemplo:
// calculadora.js
export function soma(a, b) {
return a + b;
}
export function multiplica(a, b) {
return a * b;
}
// app.js
import { soma } from './calculadora';
console.log(soma(5, 3));
Ao executar o build de produção, a função multiplica será eliminada do bundle final, pois não é utilizada.
Perguntas Técnicas Frequentes
Diferença entre ES6 Module e CommonJS
ES6 Modules são estáticamentes resolvidos em tempo de compilação — todas as importações devem estar no topo do arquivo:
import configuracao from '../config/dados.js';
// Isto causaria erro de compilação:
// if (process.env.NODE_ENV === 'development') {
// import configuracao from '../config/dados-dev.js';
// }
CommonJS é resolvido em tempo de execução, permitindo importações condicionais:
let configuracao = require('../config/dados.js');
if (process.env.NODE_ENV === 'development') {
configuracao = require('../config/dados-dev.js');
}
Como o Webpack realiza a análise estática durante a compilação (antes da execução), somente ES6 Modules permitem o Tree-Shaking efetivo.
Por que empacotar e construir o código front-end?
- Redução de体积: minificação, Tree-Shaking e compressão reduzem o tamanho dos assets
- Suporte a sintaxes modernas: transpilação de ES6+, TypeScript e SCSS para compatibilidade universal
- Compatibilidade entre navegadores: polyfills e transformações automáticas para versões legadas
- Automação do fluxo de trabalho: linting, testes e geração de documentação integrados ao pipeline
- Otimização de deploy: gera artefatos prontos para produção com cache otimizado
Module, Chunk e Bundle
- Module: qualquer arquivo no projeto — JS, CSS, imagens — tratado como módulo pelo Webpack
- Chunk: agrupamento de módulos gerado por entry points, importações dinâmicas ou
splitChunks - Bundle: o arquivo final de saída gerado após o processo de empacotamento
Loaders vs Plugins
Loaders transformam o conteúdo de arquivos individuais antes do empacotamento. O Webpack nativamente compreende apenas JavaScript e JSON; loaders como css-loader, babel-loader e sass-loader estendem essa capacidade para outros tipos de arquivo.
Plugins operam durante todo o ciclo de compilação, interceptando eventos e realizando tarefas como geração de HTML (HtmlWebpackPlugin), extração de CSS (MiniCssExtractPlugin) e otimização de código.
Babel vs Webpack
Babel é um transpilador focado em converter sintaxes JavaScript modernas para versões compatíveis. Não gerencia dependências nem módulos.
Webpack é um empacotador de módulos que gerencia dependências, assets e o pipeline de build completo. O Babel é tipicamente integrado ao Webpack via babel-loader.
Criando uma Biblioteca com Webpack
output: {
filename: 'minha-biblioteca.js',
path: path.resolve(__dirname, 'dist'),
library: 'MinhaBiblioteca',
libraryTarget: 'umd',
globalObject: 'this'
}
babel-polyfill vs core-js/babel-runtime
babel-polyfill (deprecated) modificava protótipos globais, potencialmente causando conflitos.
@babel/runtime injeta apenas os helpers necessários como módulos importados, sem poluir o escopo global. Para bibliotecas compartilhadas, é a abordagem recomendada.
Por que Proxy não pode ser polyfilled?
O Proxy intercepta operações em nível fundamental do motor JavaScript (acessos a propriedas, chamadas de função, etc.). Não existe nenhuma API nativa equivalente que permita replicar esse comportamento. Enquanto Class pode ser simulada com funções construtoras e Promise com callbacks, Object.defineProperty não consegue interceptar todas as operações que Proxy suporta.