¿Nombre de importación de variable ES6 en node.js?

108

¿Es posible importar algo en el módulo que proporciona el nombre de la variable mientras se usa la importación de ES6?

Es decir, quiero importar algún módulo en un tiempo de ejecución dependiendo de los valores proporcionados en una configuración:

import something from './utils/' + variableName;
Vytautas Butkus
fuente
1
@Bigood sí, el compilador arroja y Webstorm también muestra un error
Vytautas Butkus

Respuestas:

67

No con la importdeclaración. importyexport están definidos de tal manera que se pueden analizar estáticamente, por lo que no pueden depender de la información en tiempo de ejecución.

Está buscando la API del cargador (polyfill) , pero no tengo claro el estado de la especificación:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});
Felix Kling
fuente
3
¿Necesito "requerir" el sistema? No tiene un alcance global. Ps, estoy usando babel js
Vytautas Butkus
Aún no está implementado en ningún lado AFAIK. Tienes que usar el polyfill del enlace.
Felix Kling
1
Recién comprobado, funciona un poco (intenta cargar el archivo) pero luego estropea las rutas de otros módulos ... Lástima que no sea compatible de forma nativa ..
Vytautas Butkus
3
¿Sigue siendo un problema? Necesito cargar módulos ES6 dinámicamente pero no he tenido éxito ..
calbertts
26

Además de la respuesta de Felix , señalaré explícitamente que esto no está permitido actualmente por la gramática ECMAScript 6 :

ImportDeclaration :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • de ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

Un ModuleSpecifier solo puede ser un StringLiteral , no cualquier otro tipo de expresión como AdditiveExpression .

apsillers
fuente
2
Es una pena que esto no se haya extendido para incluir const string literals. Son analizables estáticamente, ¿no? Haría posible la reutilización de la ubicación de una dependencia. (por ejemplo, importe una plantilla y tenga disponible tanto la plantilla como la ubicación de la plantilla).
nicodemus13
26

Si bien esto no es realmente una importación dinámica (por ejemplo, en mi circunstancia, todos los archivos que estoy importando a continuación serán importados y empaquetados por paquete web, no seleccionados en tiempo de ejecución), un patrón que he estado usando y que puede ayudar en algunas circunstancias es :

import Template1 from './Template1.js';
import Template2 from './Template2.js';

const templates = {
  Template1,
  Template2
};

export function getTemplate (name) {
  return templates[name];
}

o alternativamente:

// index.js
export { default as Template1 } from './Template1';
export { default as Template2 } from './Template2';


// OtherComponent.js
import * as templates from './index.js'
...
// handy to be able to fall back to a default!
return templates[name] || templates.Template1;

No creo que pueda volver a un valor predeterminado tan fácilmente con require(), lo que arroja un error si intento importar una ruta de plantilla construida que no existe.

Se pueden encontrar buenos ejemplos y comparaciones entre requerir e importar aquí: http://www.2ality.com/2014/09/es6-modules-final.html

Excelente documentación sobre la reexportación desde @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Me interesa escuchar comentarios sobre este enfoque :)

ptim
fuente
Voto a favor. Usé el enfoque "o alternativamente". Funcionó de maravilla para mi solución de localización personalizada.
Groundh0g
1
Debería haber pensado en esto. Gran solución, no importa cómo esté importando las cosas (e incluso si no está importando nada). ¿Tiene una lista de elementos de los que desea obtener el nombre, o obtener por nombre, más adelante? Colóquelos en un literal de objeto en lugar de un literal de matriz, y deje que la sintaxis del objeto se encargue de nombrarlos según su nombre de constante / variable local. Si los necesita como una lista nuevamente, simplemente hágalo Object.values(templates).
Andrew Koster
15

Hay una nueva especificación que se denomina importación dinámica para módulos ES. Básicamente, solo llamas import('./path/file.js')y listo. La función devuelve una promesa, que se resuelve con el módulo si la importación se realizó correctamente.

async function importModule() {
   try {
      const module = await import('./path/module.js');
   } catch (error) {
      console.error('import failed');
   }
}

Casos de uso

Los casos de uso incluyen la importación de componentes basados ​​en rutas para React, Vue, etc. y la capacidad de cargar módulos de forma diferida , una vez que se requieren durante el tiempo de ejecución.

Más información

Aquí hay una explicación sobre los desarrolladores de Google .

Compatibilidad del navegador (abril de 2020)

Según MDN , es compatible con todos los principales navegadores actuales (excepto IE) y caniuse.com muestra un 87% de soporte en toda la cuota de mercado global. Nuevamente, no hay soporte en IE o Edge sin cromo.

Nicolai Schmid
fuente
¿Estás seguro de tu edición? la propuesta muestra un ejemplo con una ruta variable: github.com/tc39/proposal-dynamic-import#example
phil294
@Blauhirn Lo estaba, pero tu enlace muestra claramente que es una posibilidad. Aunque no tengo idea de cómo el paquete web, por ejemplo, resolvería estas importaciones
Nicolai Schmid
corrígeme si me equivoco, pero el paquete web no los procesa, ¿verdad? Pensé que el objetivo de las importaciones dinámicas era que se ejecutaran "tal cual" en el navegador.
phil294
Sí, puede ejecutarlos en el navegador tal cual. Pero webpack usa automáticamente las importaciones para dividir su aplicación en varios paquetes para diferentes partes de su aplicación, por ejemplo, para las rutas. Los uso todo el tiempo y son realmente útiles. Y en lo que respecta al "procesamiento"; webpack pasará las importaciones por babel, que rellenará la función para los navegadores más antiguos.
Nicolai Schmid
Para que quede claro: la importación dinámica () será trabajar con variables y no se requiere que sea estáticamente analizables (que es el punto central de 'dinámica', ¿verdad?). Mira mi respuesta.
Velojet
6

Entiendo que la pregunta se hizo específicamente para ES6 importen Node.js, pero lo siguiente podría ayudar a otros que buscan una solución más genérica:

let variableName = "es5.js";
const something = require(`./utils/${variableName}`);

Tenga en cuenta que si está importando un módulo ES6 y necesita acceder a la defaultexportación, deberá utilizar uno de los siguientes:

let variableName = "es6.js";

// Assigning
const defaultMethod = require(`./utils/${variableName}`).default;

// Accessing
const something = require(`./utils/${variableName}`);
something.default();

También puede usar la desestructuración con este enfoque que puede agregar más familiaridad de sintaxis con sus otras importaciones:

// Destructuring 
const { someMethod } = require(`./utils/${variableName}`);    
someMethod();

Desafortunadamente, si desea acceder defaultademás de desestructurar, deberá realizar esto en varios pasos:

// ES6 Syntax
Import defaultMethod, { someMethod } from "const-path.js";

// Destructuring + default assignment
const something = require(`./utils/${variableName}`);

const defaultMethod = something.default;    
const { someMethod, someOtherMethod } = something;
MCTaylor17
fuente
4

puede utilizar la notación que no es ES6 para hacer eso. esto es lo que funcionó para mí:

let myModule = null;
if (needsToLoadModule) {
  myModule = require('my-module').default;
}
mlevanon
fuente
3

Me gusta menos esta sintaxis, pero funciona: en
lugar de escribir

import memberName from "path" + "fileName"; 
// this will not work!, since "path" + "fileName" need to be string literal

usa esta sintaxis:

let memberName = require("path" + "fileName");
Gil Epshtain
fuente
1
@UlysseBN ¿Diferente en el mal sentido? ¿O de una forma que realmente no importa?
Sam
@Jacob, realmente son completamente diferentes, así que sí, podría importar dependiendo de lo que estés haciendo. La primera sintaxis se evalúa estáticamente, mientras que la segunda se evalúa dinámicamente. Entonces, por ejemplo, si está utilizando el paquete web, no será posible realizar la agitación de árboles correctamente con el segundo. Hay muchas otras diferencias, le sugiero que lea el documento y vea cuál es más apropiado para usted.
Ulysse BN
@Jacob - Realmente no importa (en la mayoría de los casos). require()es un método Node.JS para cargar archivos, que es la versión inicial. importdeclaración es la versión más reciente, que ahora forma parte de la sintaxis del idioma oficial. Sin embargo, en muchos casos el navegador utilizará el anterior (detrás de la ciencia). La declaración require también cobrará sus archivos, por lo que si un archivo se carga la segunda vez, se cargará desde la memoria (mejor rendimiento). La forma de importación tiene sus propios beneficios, si está utilizando WebPack. luego, el paquete web puede eliminar las referencias muertas (estos scripts no se descargarán en el cliente).
Gil Epshtain
1

La importación dinámica () (disponible en Chrome 63+) hará su trabajo. Así es cómo:

let variableName = 'test.js';
let utilsPath = './utils/' + variableName;
import(utilsPath).then((module) => { module.something(); });
Velojet
fuente
0

Lo haría así

function load(filePath) {
     return () => System.import(`${filePath}.js`); 
     // Note: Change .js to your file extension
}

let A = load('./utils/' + variableName)

// Now you can use A in your module
abril
fuente
0

./utils/test.js

export default () => {
  doSomething...
}

llamar desde archivo

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Andrés Muñoz
fuente