Bundler é uma ferramenta de compilação que combina múltiplos módulos JavaScript em um ou vários arquivos para uso em um navegador ou aplicativo móvel. Os bundlers modernos (Webpack, Metro, Vite) não apenas mesclam arquivos, mas também realizam transpilação, minificação e otimização de recursos. De acordo com Webpack Concepts (2026), a configuração adequada do bundler reduz o tamanho da compilação em 40–60% sem perder funcionalidade.
Pontos principais
Bundler (empacotador de módulos) é uma ferramenta de linha de comando que recebe um ponto de entrada — o arquivo principal do aplicativo — e percorre recursivamente todas as suas dependências, construindo um grafo de módulos. Na saída, o bundler gera um ou mais arquivos que podem ser incluídos em uma página HTML ou executados em um aplicativo móvel.
Por muito tempo, os navegadores não suportavam um sistema de módulos JavaScript no nível da plataforma. Bundler resolveu esse problema transformando centenas de import e require em um único arquivo. Hoje, mesmo com suporte nativo a módulos ES nos navegadores, os bundlers realizam tarefas adicionais: transpilação de JSX e TypeScript, minificação de código, code splitting para carregamento lento e hot module replacement (HMR) para acelerar o desenvolvimento.
No desenvolvimento móvel, React Native usa Metro como bundler padrão, enquanto Flutter usa seu próprio sistema de compilação baseado em Dart. A escolha do bundler afeta diretamente a velocidade de desenvolvimento, o tamanho do aplicativo e o desempenho em tempo de execução.
O empacotamento passa por várias etapas: análise de arquivos de entrada, construção do grafo de dependências, transformação de módulos e geração de arquivos de saída. Em cada etapa, o bundler aplica loaders para transformar o código-fonte e plugins para otimização.
O bundler começa pelo ponto de entrada, lê o arquivo e constrói uma AST (Árvore de Sintaxe Abstrata). Da AST, todas as declarações import e require são extraídas. Para cada importação encontrada, o bundler repete o processo recursivamente até que todas as dependências sejam coletadas em um único grafo.
// Implementação simplificada de um bundler
const fs = require('fs');
const path = require('path');
function buildGraph(entry) {
const content = fs.readFileSync(entry, 'utf-8');
const imports = content.match(/require\(['"](.+?)['"]\)/g);
const modulePath = path.resolve(path.dirname(entry), imports[0]);
return { entry, content, deps: [buildGraph(modulePath)] };
}
Após construir o grafo, o bundler aplica loaders — transformações que convertem arquivos em JavaScript: TypeScript → JS, SCSS → CSS, JSX → funções JSX. Em seguida, plugins realizam transformações adicionais: minificação, incorporação de imagens, geração de service worker.
Os bundlers são divididos em três gerações: clássicos, especializados e de nova geração baseados em módulos ES. Cada tipo tem sua própria arquitetura e área de aplicação.
Webpack é o bundler mais difundido com um ecossistema enorme. Sua principal vantagem é a flexibilidade: milhares de loaders e plugins cobrem praticamente qualquer cenário de compilação. Parcel oferece uma abordagem zero-config, detectando automaticamente as transformações necessárias com base nas extensões de arquivo. Ambos os bundlers suportam code splitting e HMR.
Metro foi criado especificamente para React Native e funciona apenas com JavaScript e TypeScript. Sua arquitetura é otimizada para desenvolvimento móvel: suporte a inline requires, carregamento assíncrono de módulos e integração com o mecanismo Hermes. Rollup é focado em bibliotecas e pacotes npm, gerando uma saída de módulos ES limpa sem wrappers desnecessários.
A escolha do bundler depende da plataforma e dos requisitos do projeto. Webpack é versátil, mas requer configuração detalhada. Metro é a única opção para React Native. Vite oferece a experiência de desenvolvimento mais rápida graças aos módulos ES nativos.
| Característica | Webpack | Metro | Vite |
|---|---|---|---|
| Plataforma | Web, universal | React Native | Web, universal |
| Velocidade de compilação dev | Média | Alta | Muito alta |
| Code splitting | Sim | Sim | Sim |
| HMR | Sim | Fast Refresh | HMR instantâneo |
| Ecossistema de plugins | Enorme | Limitado | Crescendo |
| Configuração | Complexa | Simples | Simples |
| Suporte a TypeScript | Via loaders | Integrado | Integrado |
Vite usa esbuild para pré-empacotamento de dependências e Rollup para compilações de produção, proporcionando uma melhoria de velocidade de 5 a 10 vezes em comparação com Webpack no modo de desenvolvimento. Metro não suporta alvos web, mas fornece integração perfeita com a ponte React Native e Turbo Modules.
A configuração do bundler determina como diferentes tipos de arquivo são processados, onde a compilação é gerada e quais otimizações são aplicadas. Um arquivo de configuração típico inclui o ponto de entrada, regras para módulos, plugins e configurações de saída.
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } },
},
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })],
devServer: { port: 3000, hot: true },
};
Metro usa arquivos separados metro.config.js e babel.config.js para configuração. Ao contrário do Webpack, a configuração do Metro é significativamente mais simples, pois o bundler é especializado para React Native e não requer configuração de loaders para estilos ou imagens.
Mesmo desenvolvedores experientes encontram erros de empacotamento. Duplicate module é um dos problemas mais comuns, quando a mesma dependência aparece várias vezes no bundle devido a incompatibilidade de versões no package.json. Module not found ocorre quando o caminho de importação está incorreto ou o pacote não está instalado.
Metro é limitado pelo tamanho do bundle para dispositivos móveis. Quando o limite de 2–5 MB é excedido, ocorre o erro Unable to resolve module devido à memória insuficiente no dispositivo. A solução é usar inline requires e RAM bundles para carregamento lento de módulos. Metro também é sensível a symlinks, portanto monorepos exigem configuração adicional de watchFolders.
Para Webpack, um problema típico é Module parse failed, que ocorre quando um arquivo tem formato inesperado ou o loader necessário está faltando. Verificar a extensão do arquivo e adicionar a regra apropriada em module.rules resolve o problema. Usar source-map-explorer ajuda a encontrar módulos duplicados e dependências redundantes no bundle final.
Compilações lentas são um dos principais problemas ao trabalhar com bundlers. Para acelerar, use persistent caching, que salva os resultados da transformação de módulos entre execuções. Webpack 5 suporta cache de arquivos via cache: { type: 'filesystem' }, Vite usa esbuild para pré-empacotamento e Metro depende do cache de módulos em memória. Além disso, especifique exclude para node_modules nos loaders para evitar processar pacotes já compilados.
Code splitting divide o bundle em fragmentos que são carregados sob demanda. Diferentes bundlers implementam essa estratégia de maneiras distintas. Webpack suporta importações dinâmicas import(), criando fragmentos separados para cada módulo. Metro usa inline requires para carregamento diferido. Vite divide automaticamente vendor chunks e importações dinâmicas sem configuração adicional. A escolha da estratégia depende dos requisitos do projeto: para aplicativos móveis, bundles iniciais menores com carregamento lento são preferíveis.
Para o code splitting funcionar corretamente no React Native, é necessária configuração adicional: o Metro deve ser configurado para trabalhar com fragmentos assíncronos via inlineRequires. No Webpack e Vite, o code splitting funciona prontamente ao usar a sintaxe dinâmica import(), que cria automaticamente pontos de divisão. Para máxima eficiência, combine code splitting com dicas de pré-carregamento via <link rel="preload"> para fragmentos críticos.
Perguntas frequentes
Para projetos simples sem JSX, TypeScript ou módulos CSS, você pode usar módulos ES nativos no navegador. No entanto, para compilações de produção, um bundler fornece minificação, tree shaking e code splitting, que são críticos para o desempenho. Sem um bundler, é difícil manter uma arquitetura modular em projetos grandes.
Para React Native — Metro (a única opção suportada). Para aplicações web — Vite para novos projetos (devido à velocidade) ou Webpack para existentes com ecossistema rico. Para bibliotecas e pacotes npm — Rollup, pois gera saída de módulos ES limpa.
O bundler determina diretamente o tamanho do bundle JS final. Webpack com tree shaking pode reduzir o tamanho em 30–50%. Metro para React Native suporta bytecode Hermes, que reduz o tamanho em 20–30% em comparação com JavaScript comum. Code splitting permite carregar módulos sob demanda, reduzindo o bundle inicial.
Para a web, o bundler gera código compatível com diferentes navegadores e suporta CSS, imagens e fontes. Para plataformas móveis, Metro gera código para um mecanismo JavaScript (Hermes ou JSC), não processa CSS ou HTML e otimiza a compilação para os recursos limitados do dispositivo.
Sim, alguns projetos usam Webpack para a parte web e Metro para React Native — por exemplo, em monorepos com base de código compartilhada. No entanto, isso complica a configuração de CI/CD e requer sincronização de versões de dependências. Recomenda-se usar um único bundler para todos os alvos, se possível.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também