Módulos ES6: import, export e module bundling
1. Introdução aos Módulos ES6
Antes da chegada dos módulos ES6, a organização de código em JavaScript era um desafio. Desenvolvedores dependiam de scripts globais, onde cada arquivo adicionava variáveis ao escopo global, causando conflitos e poluição. Soluções como IIFEs (Immediately Invoked Function Expressions) e padrões de módulo improvisados tentavam contornar o problema, mas eram frágeis e difíceis de manter.
Os módulos ES6 (ECMAScript 2015) revolucionaram a forma como estruturamos código JavaScript. Eles introduziram um sistema oficial de módulos com escopo próprio, executado em strict mode automaticamente. Diferente de scripts tradicionais, módulos não poluem o escopo global — cada módulo tem seu próprio contexto de execução. Variáveis declaradas dentro de um módulo não são acessíveis externamente a menos que explicitamente exportadas.
2. Exportando com export
O sistema de módulos ES6 oferece duas formas principais de exportação: named exports e default exports.
Named exports permitem exportar múltiplos valores de um módulo:
// utils/matematica.js
export const PI = 3.14159;
export function somar(a, b) {
return a + b;
}
export class Calculadora {
constructor() {
this.historico = [];
}
calcular(operacao, a, b) {
this.historico.push({ operacao, a, b });
return this[operacao](a, b);
}
}
Default export é usado para exportar um valor principal por módulo:
// components/Botao.js
export default function Botao({ texto, onClick }) {
return <button onClick={onClick}>{texto}</button>;
}
É possível combinar ambos no mesmo módulo e renomear exports com as:
// utils/helpers.js
function formatarData(data) { /* ... */ }
function formatarMoeda(valor) { /* ... */ }
export { formatarData as dataFormatada, formatarMoeda };
export default function log(mensagem) {
console.log(`[LOG]: ${mensagem}`);
}
3. Importando com import
A sintaxe de importação é flexível e poderosa. Para named exports, usamos chaves com os nomes exatos:
import { PI, somar, Calculadora } from './utils/matematica.js';
console.log(somar(2, 3)); // 5
Default exports são importados sem chaves, podendo receber qualquer nome:
import Botao from './components/Botao.js';
Importação mista e renomeação com as:
import log, { dataFormatada as df, formatarMoeda } from './utils/helpers.js';
Para importar todo o módulo como um namespace:
import * as Matematica from './utils/matematica.js';
console.log(Matematica.PI); // 3.14159
4. Módulos ES6 no Node.js
O Node.js adotou oficialmente os módulos ES6 a partir da versão 12. Para habilitá-los, configure o package.json:
{
"type": "module"
}
Alternativamente, use a extensão .mjs para arquivos ESModules. Com "type": "module", arquivos .js são tratados como ESModules. A importação dinâmica com import() funciona em ambos os sistemas:
// Exemplo de importação dinâmica
async function carregarModulo() {
const modulo = await import('./utils/pesado.js');
modulo.executar();
}
Diferenças importantes: require não está disponível em ESModules (use createRequire do módulo module se necessário), e o escopo de módulo é strict mode por padrão.
5. Módulos ES6 no React
No React, export default é padrão para componentes, enquanto export named é ideal para hooks customizados e utilitários:
// components/UsuarioCard.jsx
export default function UsuarioCard({ usuario }) {
return (
<div className="card">
<h3>{usuario.nome}</h3>
<p>{usuario.email}</p>
</div>
);
}
// hooks/useAuth.js
export function useAuth() {
const [user, setUser] = useState(null);
// lógica de autenticação
return { user, login, logout };
}
// utils/validacao.js
export function validarEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
A organização de pastas típica em projetos React separa componentes, páginas, hooks e utilitários, facilitando imports limpos. Bibliotecas externas como React e ReactDOM são importadas diretamente:
import React from 'react';
import { useState, useEffect } from 'react';
Essa estrutura permite que bundlers realizem tree shaking, removendo código não utilizado.
6. Module Bundling: Conceitos e Ferramentas
Module bundling é o processo de combinar múltiplos módulos em um ou poucos arquivos otimizados para produção. Isso é necessário porque navegadores mais antigos não suportam ESModules nativamente, e mesmo os modernos se beneficiam de bundles menores para performance.
Webpack é o bundler mais tradicional para React. Uma configuração básica inclui:
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: 'babel-loader',
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })],
};
Vite é um bundler moderno que usa ESBuild para desenvolvimento rápido e Rollup para produção. Sua configuração para React é minimalista:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
Comparação rápida: Webpack é maduro e altamente configurável, Vite oferece HMR (Hot Module Replacement) instantâneo, Parcel é zero-config, e esbuild é extremamente rápido mas menos flexível.
7. Tree Shaking e Otimização de Bundles
Tree shaking é a remoção de código morto durante o bundle, possível graças à natureza estática dos ESModules. Para escrever código amigável ao tree shaking:
// ❌ Ruim para tree shaking
import { utils } from './utils';
utils.formatarData(data); // Carrega todo o módulo
// ✅ Bom para tree shaking
import { formatarData } from './utils/formatacao.js';
formatarData(data); // Apenas o necessário é incluído
Configure sideEffects no package.json para informar ao bundler quais arquivos têm efeitos colaterais:
{
"sideEffects": [
"./src/estilos/global.css",
"*.scss"
]
}
Ferramentas como webpack-bundle-analyzer e vite inspect ajudam a visualizar o tamanho do bundle e identificar oportunidades de otimização.
8. Boas Práticas e Padrões com Módulos ES6
Evite dependências circulares: quando o módulo A importa B e B importa A, podem ocorrer erros de inicialização. Refatore extraindo dependências comuns para um terceiro módulo.
Use barrel files (index.js) para reexportação organizada:
// components/index.js
export { default as Botao } from './Botao';
export { default as Card } from './Card';
export { default as Modal } from './Modal';
Import dinâmico para code splitting com React.lazy e Suspense:
import React, { Suspense, lazy } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<Suspense fallback={<div>Carregando...</div>}>
<Dashboard />
</Suspense>
);
}
Diferenças práticas entre CommonJS e ESModules: CommonJS usa require/module.exports, carregamento síncrono e cópia de valor. ESModules usam import/export, carregamento assíncrono e ligações ao vivo (live bindings). Em projetos Node.js + React, prefira ESModules sempre que possível para aproveitar tree shaking e melhor integração com ferramentas modernas.
Os módulos ES6 transformaram a forma como escrevemos JavaScript, trazendo clareza, reusabilidade e performance. Dominar import, export e module bundling é essencial para qualquer desenvolvedor que trabalhe com Node.js e React.
Referências
- MDN Web Docs: import — Documentação oficial da Mozilla sobre a sintaxe de importação em JavaScript.
- MDN Web Docs: export — Guia completo sobre a sintaxe de exportação, incluindo named e default exports.
- Node.js Documentation: ES Modules — Documentação oficial do Node.js sobre configuração e uso de módulos ES6.
- Webpack Documentation: Code Splitting — Guia oficial do Webpack sobre code splitting e lazy loading com import() dinâmico.
- Vite Documentation: Features — Documentação do Vite explicando suporte a ESModules, HMR e configuração para React.
- React Documentation: Code-Splitting — Guia oficial do React sobre React.lazy, Suspense e importação dinâmica de componentes.
- Tree Shaking com Webpack — Tutorial oficial do Webpack sobre configuração de tree shaking e sideEffects.