Guia Completo de Configuração e Otimização do Webpack

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.

Tags: webpack babel postcss mini-css-extract-plugin tree-shaking

Publicado em 6-24 18:50