JavaScript export e import
Aprenda export e import no JavaScript: exportações nomeadas, padrão, renomeação com as, importações de namespace, dinâmicas e reexportações.
No JavaScript moderno, as palavras-chave export e import são a forma de dividir um programa em vários arquivos e depois uni-los novamente. Um arquivo que as utiliza é um módulo ES: em vez de colocar tudo em um único script gigante, você mantém cada parte da funcionalidade em seu próprio arquivo, declara o que compartilha com o mundo externo (export) e importa apenas o que precisa em outros lugares (import).
Este guia cobre todas as formas que você encontrará na prática — exportações nomeadas, exportações padrão, renomeação com as, importações de namespace, reexportação e import() dinâmico — além dos erros comuns que pegam as pessoas de surpresa. Cada exemplo é pequeno e executável.
Por que usar módulos?
Antes dos módulos ES, compartilhar código significava adicionar coisas ao objeto global e torcer para que os nomes não colidissem. Os módulos resolvem isso:
- Encapsulamento — qualquer coisa que você não exportar permanece privada ao arquivo.
- Dependências explícitas — as linhas de
importno topo de um arquivo são uma lista precisa do que ele depende. - Avaliação única — o código de nível superior de um módulo é executado apenas uma vez, independentemente de quantos arquivos o importem; o resultado é armazenado em cache e compartilhado.
Para executar módulos no navegador, carregue seu arquivo de entrada com type="module":
<script src="app.js" type="module"></script>No Node.js, nomeie os arquivos com .mjs ou defina "type": "module" no package.json.
Exportações nomeadas
Uma exportação nomeada disponibiliza um vínculo com seu próprio nome. Um módulo pode ter quantas exportações nomeadas quiser.
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// You can also list exports at the bottom:
const subtract = (a, b) => a - b;
export { subtract };Você importa exportações nomeadas entre chaves, usando exatamente os mesmos nomes:
// app.js
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159Os nomes devem corresponder à exportação. import { Add } quando a exportação é add lança um SyntaxError — não existe tal exportação nomeada.
Renomeando com as
Quando dois módulos exportam o mesmo nome, ou um nome é inconveniente, renomeie na importação (ou exportação) com as:
// Rename on import
import { add as sum } from './math.js';
console.log(sum(1, 1)); // 2// Rename on export
const internalAdd = (a, b) => a + b;
export { internalAdd as add };Exportações padrão
Uma exportação padrão é a única coisa "principal" que um módulo fornece. Um módulo pode ter no máximo uma exportação padrão.
// multiply.js
export default function multiply(a, b) {
return a * b;
}Ao importar um padrão, você não usa chaves e escolhe o nome local você mesmo:
// app.js
import multiply from './multiply.js'; // any name works
console.log(multiply(4, 5)); // 20Como quem importa nomeia, as exportações padrão são frequentemente usadas quando um arquivo representa uma única coisa — uma única classe, um único componente React, um único objeto de configuração.
Misturando exportações padrão e nomeadas
Um módulo pode ter ambas. O padrão fica fora das chaves, as nomeadas ficam dentro:
// user.js
export default class User { /* ... */ }
export const ADMIN_ROLE = 'admin';// app.js
import User, { ADMIN_ROLE } from './user.js';Prefira exportações nomeadas por padrão. Elas tornam as importações autodocumentadas e permitem que as ferramentas (autocomplete, "encontrar referências", tree-shaking) funcionem de forma confiável. Recorra a uma exportação padrão apenas quando um arquivo realmente exporta uma única coisa.
Técnicas de importação
Importação de namespace (import * as)
Para capturar tudo o que um módulo exporta como um único object, use uma importação de namespace:
// app.js
import * as math from './math.js';
console.log(math.add(5, 3)); // 8
console.log(math.PI); // 3.14159Cada exportação nomeada torna-se uma propriedade de math. Uma exportação padrão, se houver, aparece como math.default. Use isso quando um módulo agrupa muitos auxiliares relacionados.
Importação dinâmica (import())
A instrução import acima é estática — ela é executada antes de qualquer outra coisa e seu caminho deve ser um literal de string. Às vezes você quer carregar um módulo sob demanda: apenas quando o usuário abre um recurso, ou com base em uma condição de runtime. Para isso, use import() como função. Ela retorna uma Promise que resolve para o object de namespace do módulo:
// Load a heavy module only when needed
async function openEditor() {
const editor = await import('./editor.js');
editor.init();
}A importação dinâmica funciona dentro de funções e condicionais — lugares onde um import estático não é permitido — o que a torna a base para code-splitting e lazy loading.
Reexportação
Um arquivo "barrel" pode coletar exportações de vários módulos e encaminhá-las, para que os consumidores importem de um único lugar:
// shapes/index.js
export { Circle } from './circle.js';
export { Square } from './square.js';
export { default as Triangle } from './triangle.js'; // re-export a default as a name// app.js
import { Circle, Square, Triangle } from './shapes/index.js';Erros comuns
- Extensões importam no navegador e no Node.js ESM.
import { x } from './math'falha; escreva'./math.js'. import/exportsó funcionam no nível superior de um módulo — não dentro de um blocoifou de uma função. Useimport()dinâmico para carregamento condicional.- Importações são vínculos ao vivo somente leitura. Você pode ler um valor importado, mas não reatribuí-lo;
add = 5em uma importação lança um erro. - Uma exportação padrão não recebe automaticamente um nome.
export default addexporta o valor, não o nomeadd; quem importa escolhe o nome local. - Os módulos são adiados e executados automaticamente no modo estrito — sem necessidade de
'use strict'.
Um exemplo prático: um mini sistema de biblioteca
Aqui, vários módulos exportam um array de objects de livros, e um arquivo principal os importa e combina.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Interactive Library App</title>
</head>
<body>
<h1>Interactive Library App</h1>
<button id="loadFiction">Load Fiction</button>
<button id="loadSciFi">Load Sci-Fi</button>
<div id="bookList"></div>
<script src="app.js" type="module"></script>
</body>
</html>fiction.js:
export const fictionBooks = [
{ title: 'Pride and Prejudice', author: 'Jane Austen' },
{ title: 'To Kill a Mockingbird', author: 'Harper Lee' }
];sciFi.js:
export const sciFiBooks = [
{ title: 'Dune', author: 'Frank Herbert' },
{ title: 'Neuromancer', author: 'William Gibson' }
];app.js:
import { fictionBooks } from './fiction.js';
import { sciFiBooks } from './sciFi.js';
function displayBooks(books) {
const list = document.getElementById('bookList');
list.innerHTML = '';
books.forEach(book => {
const item = document.createElement('div');
item.textContent = `${book.title} by ${book.author}`;
list.appendChild(item);
});
}
document
.getElementById('loadFiction')
.addEventListener('click', () => displayBooks(fictionBooks));
document
.getElementById('loadSciFi')
.addEventListener('click', () => displayBooks(sciFiBooks));Cada categoria vive em seu próprio módulo e exporta uma função ou dados, e app.js os une. Dividir dados e comportamento dessa forma mantém cada arquivo pequeno e torna trivial adicionar novas categorias.
Boas práticas
- Prefira exportações nomeadas para que as importações se autodocumentem e o tree-shaking funcione.
- Uma responsabilidade por módulo — um arquivo deve ser fácil de descrever em uma frase.
- Use arquivos barrel com moderação — eles organizam as importações, mas podem prejudicar o tree-shaking se usados em excesso.
- Carregue sob demanda código pesado ou raramente usado com
import()dinâmico. - Sempre inclua extensões de arquivo em caminhos relativos.
Conclusão
As exportações nomeadas fornecem muitos vínculos por arquivo, as exportações padrão fornecem uma exportação "principal", e import (com as, * as e import() dinâmico) oferece controle preciso sobre o que entra e quando. Juntos, eles transformam um conjunto de scripts em um grafo de módulos sustentável e sujeito a tree-shaking. A seguir, veja como os módulos são carregados no navegador em Módulos, introdução.