ES6: Declaraciones de importación condicionales y dinámicas

85

Condicional

¿Es posible tener declaraciones de importación condicionales como a continuación?

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

He intentado lo anterior pero obtengo el siguiente error (de Babel) al compilar.

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

Dinámica

¿Es posible tener declaraciones de importación dinámicas como las siguientes?

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

Lo anterior recibe el mismo error de Babel durante la compilación.

¿Es posible hacer esto o hay algo que me falta?

Razonamiento

La razón por la que intento hacer esto es que tengo muchas importaciones para varias "páginas" y siguen un patrón similar. Me gustaría limpiar mi base de código importando estos archivos con un bucle for dinámico.

Si esto no es posible, ¿existe una mejor manera de manejar una gran cantidad de importaciones en ES6?

Enijar
fuente
1
¿No se puede usar la herencia en tal caso? utilizar superpara llamar específico.
Jai
Ya estoy usando la herencia, pero estas "páginas" contienen lógica específica de "página" en ellas. Tengo una clase de "página" base que se extiende todas, pero esto no es suficiente para limpiar la gran cantidad de importaciones que tengo.
Enijar
1
@zerkms: No se levantan de bloques, son errores de sintaxis.
Bergi

Respuestas:

54

Tenemos propuesta de importaciones dinámicas ahora con ECMA. Esto se encuentra en la etapa 2. También está disponible como babel-preset .

La siguiente es una forma de hacer una representación condicional según su caso.

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

Esto básicamente devuelve una promesa. Se espera que la resolución de la promesa tenga el módulo. La propuesta también tiene cosas como múltiples importaciones dinámicas, importaciones predeterminadas, importación de archivos js, etc. Puede encontrar más información sobre importaciones dinámicas aquí .

thecodejack
fuente
3
Esta. Las importaciones dinámicas son el camino a seguir. Funcionan como un require (), excepto que le dan una promesa en lugar de un módulo.
superluminario
24

No puede resolver dinámicamente sus dependencias, ya que importsestán diseñadas para el análisis estático. Sin embargo, probablemente pueda usar algunos requireaquí, algo como:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}
Jonathan Petitcolas
fuente
8
"ya que las importaciones están destinadas al análisis estático" --- esta declaración es vaga. importLos s están diseñados para importar, no para análisis.
zerkms
12
@zerkms: creo que lo que querían decir es que las importdeclaraciones están destinadas a ser adecuadas para el análisis estático, porque nunca son condicionales, las herramientas pueden analizar los árboles de dependencia más fácilmente.
Joe Clay
4
Difícil de entender con "foo", "baz" y "bar". ¿Qué tal un ejemplo de la vida real?
TetraDev
1
Esto ya no es verdad. Las importaciones dinámicas son ahora una cosa. Vea aquí: stackoverflow.com/a/46543949/687677
superluminario
7

Como esta pregunta tiene una alta calificación de Google, vale la pena señalar que las cosas han cambiado desde que se publicaron las respuestas anteriores.

MDN tiene esta entrada en Importaciones dinámicas :

La palabra clave de importación se puede llamar como una función para importar dinámicamente un módulo. Cuando se usa de esta manera, devuelve una promesa.

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');

Se puede encontrar un artículo útil sobre el tema en Medium .

LeeGee
fuente
1

Require no resolverá su problema, ya que es una llamada sincrónica. Hay varias opciones y todas involucran

  1. Solicitando el módulo que necesitas
  2. Esperando una promesa de devolver el módulo

En ECMA Script hay soporte para módulos de carga diferida usando SystemJS. Por supuesto, esto no es compatible con todos los navegadores, por lo que, mientras tanto, puede usar JSPM o un shim SystemJS.

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

Henrik Vendelbo
fuente
1

Desde 2016 han pasado muchas cosas en el mundo de JavaScript, por lo que creo que es hora de ofrecer la información más actualizada sobre este tema. Actualmente, las importaciones dinámicas son una realidad tanto en Node como en los navegadores (de forma nativa si no le importa IE, o con @ babel / plugin-syntax-dynamic-import si le importa).

Por lo tanto, considere un módulo de muestra something.jscon dos exportaciones con nombre y una exportación predeterminada:

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

Podemos usar la import()sintaxis para cargarlo condicionalmente de manera fácil y limpia:

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
  })
}

Pero como el retorno es a Promise, el azúcar sintáctico async/ awaittambién es posible:

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

¡Ahora piense en las posibilidades junto con la asignación de destrucción de objetos ! Por ejemplo, podemos poner fácilmente solo una de esas exportaciones nombradas en la memoria para uso posterior:

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

O tal vez tome una de esas exportaciones con nombre y cámbiele el nombre a cualquier otra cosa que deseemos:

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

O incluso cambie el nombre de la función exportada predeterminada a algo que tenga más sentido:

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

Solo una última (pero no menos importante) nota: import() puede parecer una llamada de función, pero no es una Function. Es una sintaxis especial que simplemente usa paréntesis (similar a lo que sucede con super()). Entonces no es posible asignar importa una variable o usar cosas del Functionprototipo, como call/ apply.

Erick Petrucelli
fuente