ES6: Declarações de importação condicionais e dinâmicas

86

Condicional

É possível ter instruções de importação condicionais como a seguir?

if (foo === bar) {
    import Baz from './Baz';
}

Tentei o procedimento acima, mas recebo o seguinte erro (de Babel) ao compilar.

'import' and 'export' may only appear at the top level

Dinâmico

É possível ter instruções de importação dinâmicas como a seguir?

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        import Baz from `./${foo}`;
    }
}

O exemplo acima recebe o mesmo erro do Babel durante a compilação.

Isso é possível fazer ou há algo que estou perdendo?

Raciocínio

Estou tentando fazer isso porque tenho muitas importações de várias "páginas" e elas seguem um padrão semelhante. Eu gostaria de limpar minha base de código importando esses arquivos com um loop for dinâmico.

Se isso não for possível, existe uma maneira melhor de lidar com um grande número de importações no ES6?

Enijar
fonte
1
a herança não pode ser usada nesse caso? use superpara chamar específico.
Jai
Já estou usando herança, mas essas "páginas" contêm uma lógica específica de "página". Eu tenho uma classe de "página" base que todas estendem, mas isso não é suficiente para limpar o grande número de importações que tenho.
Enijar
1
@zerkms: Eles não são retirados dos blocos - são erros de sintaxe.
Bergi,
possível duplicata do nome de importação da variável ES6 em node.js ?
Bergi,

Respostas:

54

Temos uma proposta de importação dinâmica agora com ECMA. Isso está no estágio 2. Isso também está disponível como predefinição de babel .

A seguir está uma maneira de fazer a renderização condicional de acordo com o seu caso.

if (foo === bar) {
    import('./Baz')
    .then((Baz) => {
       console.log(Baz.Baz);
    });
}

Isso basicamente retorna uma promessa. Espera-se que a resolução da promessa tenha o módulo. A proposta também tem coisas como várias importações dinâmicas, importações padrão, importação de arquivo js etc. Você pode encontrar mais informações sobre importações dinâmicas aqui .

theecodejack
fonte
3
Este. As importações dinâmicas são o caminho a percorrer. Eles funcionam como um require (), exceto que oferecem uma promessa ao invés de um módulo.
superluminário de
25

Você não pode resolver dinamicamente suas dependências, pois importssão destinadas à análise estática. No entanto, você provavelmente pode usar alguns requireaqui, algo como:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}
Jonathan Petitcolas
fonte
8
"já que as importações são destinadas à análise estática." --- esta declaração é vaga. imports são projetados para importação, não para análise.
zerkms
13
@zerkms - Acho que o que eles quiseram dizer é que as importinstruções devem ser adequadas para análise estática - porque nunca são condicionais, as ferramentas podem analisar as árvores de dependência mais facilmente.
Joe Clay
4
Difícil de entender com "foo" "baz" e "bar" - que tal um exemplo da vida real?
TetraDev
1
Isso não é mais verdade. As importações dinâmicas agora são uma coisa. Veja aqui: stackoverflow.com/a/46543949/687677
superluminário
7

Como essa questão é bem avaliada pelo Google, vale ressaltar que as coisas mudaram desde que as respostas mais antigas foram postadas.

MDN tem esta entrada em Importações dinâmicas :

A palavra-chave import pode ser chamada como uma função para importar dinamicamente um módulo. Quando usado dessa forma, ele retorna uma promessa.

import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

// This form also supports the await keyword.
let module = await import('/modules/my-module.js');

Um artigo útil sobre o assunto pode ser encontrado no Medium .

LeeGee
fonte
2

Desde 2016 muito se passou no mundo do JavaScript, então acredito que é hora de oferecer as informações mais atualizadas sobre esse assunto. Atualmente, as importações dinâmicas são uma realidade tanto no Node quanto nos navegadores (nativamente, se você não se importa com o IE, ou com @ babel / plugin-syntax-dynamic-import se você se importa).

Portanto, considere um módulo de amostra something.jscom duas exportações nomeadas e uma exportação padrão:

export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')

Podemos usar a import()sintaxe para carregá-lo de forma fácil e limpa condicionalmente:

if (somethingIsTrue) {
  import('./something.js').then((module) => {
    // Use the module the way you want, as:
    module.hi('Erick') // Named export
    module.bye('Erick') // Named export
    module.default() // Default export
  })
}

Mas como o retorno é um Promise, o açúcar sintático async/ awaittambém é possível:

async imAsyncFunction () {
  if (somethingIsTrue) {
    const module = await import('./something.js')
    module.hi('Erick')
  }
}

Agora pense nas possibilidades junto com a Atribuição de Destruturação de Objetos ! Por exemplo, podemos facilmente colocar apenas uma dessas exportações nomeadas na memória para uso posterior:

const { bye } = await import('./something.js')
bye('Erick')

Ou talvez pegue uma das exportações nomeadas e renomeie para qualquer outra coisa que quisermos:

const { hi: hello } = await import('./something.js')
hello('Erick')

Ou até mesmo renomeie a função exportada padrão para algo que faça mais sentido:

const { default: helloWorld } = await import('./something.js')
helloWorld()

Apenas uma última (mas não menos importante) observação: import() pode parecer uma chamada de função, mas não é um Function. É uma sintaxe especial que usa parênteses (semelhante ao que acontece com super()). Portanto, não é possível atribuir importa uma variável ou usar coisas do Functionprotótipo, como call/ apply.

Erick Petrucelli
fonte
1

Require não resolverá seu problema, pois é uma chamada síncrona. Existem várias opções e todas envolvem

  1. Pedindo o módulo que você precisa
  2. Aguardando promessa de devolução do módulo

No script ECMA, há suporte para módulos de carregamento lento usando SystemJS. É claro que isso não é compatível com todos os navegadores, portanto, enquanto isso, você pode usar JSPM ou um shim SystemJS.

https://github.com/ModuleLoader/es6-module-loader

Henrik Vendelbo
fonte