Importação Dinâmica `import()`
Importação Dinâmica import()
introduz uma nova forma semelhante a uma função de import
que desbloqueia novas capacidades em comparação com a importação estática. Este artigo compara os dois e oferece uma visão geral do que há de novo.
Importação estática import
(recapitulação)
O Chrome 61 foi lançado com suporte para a instrução import
do ES2015 dentro de módulos.
Considere o seguinte módulo, localizado em ./utils.mjs
:
// Exportação padrão
export default () => {
console.log('Olá da exportação padrão!');
};
// Exportação nomeada `doStuff`
export const doStuff = () => {
console.log('Fazendo coisas…');
};
Aqui está como importar e usar o módulo ./utils.mjs
estaticamente:
<script type="module">
import * as module from './utils.mjs';
module.default();
// → registra 'Olá da exportação padrão!'
module.doStuff();
// → registra 'Fazendo coisas…'
</script>
Nota: O exemplo anterior usa a extensão .mjs
para sinalizar que é um módulo em vez de um script regular. Na Web, extensões de arquivo não importam muito, desde que os arquivos sejam servidos com o tipo MIME correto (por exemplo, text/javascript
para arquivos JavaScript) no cabeçalho HTTP Content-Type
.
A extensão .mjs
é especialmente útil em outras plataformas como Node.js e d8
, onde não há conceito de tipos MIME ou outros ganchos obrigatórios como type="module"
para determinar se algo é um módulo ou um script comum. Estamos usando a mesma extensão aqui para consistência entre plataformas e para distinguir claramente entre módulos e scripts comuns.
Essa forma sintática para importar módulos é uma declaração estática: ela só aceita um literal de string como especificador de módulo e introduz vinculações no escopo local via um processo de "ligação" pré-runtime. A sintaxe de import
estática só pode ser usada no nível superior do arquivo.
import
estático permite casos de uso importantes, como análise estática, ferramentas de empacotamento e eliminação de código morto (tree-shaking).
Em alguns casos, é útil:
- importar um módulo sob demanda (ou condicionalmente)
- calcular o especificador do módulo em tempo de execução
- importar um módulo dentro de um script comum (em vez de um módulo)
Nenhuma dessas opções é possível com a import
estática.
import()
Dinâmico 🔥
import()
Dinâmico introduz uma nova forma semelhante a uma função de import
que atende a esses casos de uso. import(moduleSpecifier)
retorna uma promessa para o objeto de namespace do módulo solicitado, que é criado após o carregamento, a instanciação e a avaliação de todas as dependências do módulo, bem como do próprio módulo.
Aqui está como importar e usar o módulo ./utils.mjs
dinamicamente:
<script type="module">
const moduleSpecifier = './utils.mjs';
import(moduleSpecifier)
.then((module) => {
module.default();
// → registra 'Olá da exportação padrão!'
module.doStuff();
// → registra 'Fazendo coisas…'
});
</script>
Como import()
retorna uma promessa, é possível usar async
/await
em vez do estilo de callback baseado em then
:
<script type="module">
(async () => {
const moduleSpecifier = './utils.mjs';
const module = await import(moduleSpecifier)
module.default();
// → registra 'Olá da exportação padrão!'
module.doStuff();
// → registra 'Fazendo coisas…'
})();
</script>
Nota: Embora import()
pareça uma chamada de função, ele é especificado como sintaxe que por acaso usa parênteses (semelhante ao super()
). Isso significa que import
não herda de Function.prototype
, então você não pode usá-lo com call
ou apply
, e coisas como const importAlias = import
não funcionam — aliás, import
nem é um objeto! Porém, isso não faz diferença prática.
Aqui está um exemplo de como o import()
dinâmico permite carregar módulos sob demanda ao navegar em uma pequena aplicação de página única:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Minha biblioteca</title>
<nav>
<a href="books.html" data-entry-module="books">Livros</a>
<a href="movies.html" data-entry-module="movies">Filmes</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>Este é um espaço reservado para o conteúdo que será carregado sob demanda.</main>
<script>
const main = document.querySelector('main');
const links = document.querySelectorAll('nav > a');
for (const link of links) {
link.addEventListener('click', async (event) => {
event.preventDefault();
try {
const module = await import(`/${link.dataset.entryModule}.mjs`);
// O módulo exporta uma função chamada `loadPageInto`.
module.loadPageInto(main);
} catch (error) {
main.textContent = error.message;
}
});
}
</script>
As capacidades de carregamento dinâmico habilitadas pelo import()
dinâmico podem ser bastante poderosas quando aplicadas corretamente. Para fins de demonstração, Addy modificou um exemplo de PWA do Hacker News que importava todos os seus dependentes estaticamente, incluindo comentários, na primeira carga. A versão atualizada usa o import()
dinâmico para carregar os comentários de maneira preguiçosa, evitando o custo de carregamento, análise e compilação até que o usuário realmente precise deles.
Nota: Se seu aplicativo importa scripts de outro domínio (seja estaticamente ou dinamicamente), os scripts precisam ser retornados com cabeçalhos CORS válidos (como Access-Control-Allow-Origin: *
). Isso ocorre porque, ao contrário dos scripts regulares, os scripts de módulos (e suas importações) são buscados com CORS.
Recomendações
import
estático e import()
dinâmico são ambos úteis. Cada um tem seus próprios casos de uso muito distintos. Use import
s estáticos para dependências de pintura inicial, especialmente para conteúdo acima da dobra. Em outros casos, considere carregar dependências sob demanda com import()
dinâmico.