Nom d'importation de variable ES6 dans node.js?

108

est-il possible d'importer quelque chose dans le module fournissant un nom de variable tout en utilisant l'importation ES6?

Ie je veux importer un module à un runtime en fonction des valeurs fournies dans une configuration:

import something from './utils/' + variableName;
Vytautas Butkus
la source
1
@Bigood oui, le compilateur lève et Webstorm affiche également une erreur
Vytautas Butkus

Réponses:

67

Pas avec la importdéclaration. importet exportsont définis de manière à pouvoir être analysés statiquement, ils ne peuvent donc pas dépendre des informations d'exécution.

Vous recherchez l' API du chargeur (polyfill) , mais je ne suis pas certain du statut de la spécification:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});
Félix Kling
la source
3
dois-je "exiger" le système? Ce n'est pas sur une portée globale .. Ps J'utilise babel js
Vytautas Butkus
Il n'est encore implémenté nulle part AFAIK. Vous devez utiliser le polyfill du lien.
Felix Kling
1
Juste vérifié, ça marche un peu (essaie de charger le fichier) mais ensuite gâche les chemins des autres modules ... Dommage que ce ne soit pas supporté nativement ..
Vytautas Butkus
3
Est-ce toujours un problème? J'ai besoin de charger les modules ES6 de manière dynamique mais je n'ai pas eu de succès ..
calbertts
26

En plus de la réponse de Felix , je noterai explicitement que cela n'est actuellement pas autorisé par la grammaire ECMAScript 6 :

ImportDeclaration :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • depuis ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

Un ModuleSpecifier ne peut être qu'un StringLiteral , pas un autre type d'expression comme AdditiveExpression .

apsillers
la source
2
C'est dommage que cela n'ait pas été étendu pour inclure l' const string literalart. Ils sont statiquement analysables, n'est-ce pas? Cela rendrait possible la réutilisation de l'emplacement d'une dépendance. (par exemple, importez un modèle et ayez le modèle et l'emplacement du modèle disponibles).
nicodemus13
26

Bien que ce ne soit pas réellement une importation dynamique (par exemple, dans mon cas, tous les fichiers que j'importe ci-dessous seront importés et regroupés par webpack, non sélectionné au moment de l'exécution), un modèle que j'ai utilisé qui peut aider dans certaines circonstances est :

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

const templates = {
  Template1,
  Template2
};

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

Ou bien:

// 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;

Je ne pense pas que je puisse revenir à une valeur par défaut aussi facilement avec require(), ce qui génère une erreur si j'essaie d'importer un chemin de modèle construit qui n'existe pas.

De bons exemples et comparaisons entre require et import peuvent être trouvés ici: http://www.2ality.com/2014/09/es6-modules-final.html

Excellente documentation sur la réexportation depuis @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Je suis intéressé d'entendre des commentaires sur cette approche :)

ptim
la source
Vote positif. J'ai utilisé l'approche «ou alternativement». A fonctionné comme un charme pour ma solution de localisation personnalisée.
groundh0g
1
J'aurais dû y penser. Excellente solution, peu importe comment vous importez des choses (et même si vous n'importez rien). Vous avez une liste d'éléments dont vous souhaitez obtenir le nom ou obtenir par nom plus tard? Mettez-les dans un littéral d'objet au lieu d'un littéral de tableau, et laissez la syntaxe de l'objet prendre soin de les nommer en fonction de leur nom de constante / variable locale. Si vous en avez à nouveau besoin sous forme de liste, faites-le Object.values(templates).
Andrew Koster le
15

Il existe une nouvelle spécification appelée importation dynamique pour les modules ES. En gros, vous venez d'appeler import('./path/file.js')et vous êtes prêt à partir. La fonction renvoie une promesse, qui résout avec le module si l'importation a réussi.

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

Cas d'utilisation

Les cas d'utilisation incluent l' importation de composants basés sur les routes pour React, Vue, etc. et la possibilité de charger paresseusement des modules , une fois qu'ils sont requis pendant l'exécution.

Informations complémentaires

Voici une explication sur les développeurs Google .

Compatibilité du navigateur (avril 2020)

Selon MDN, il est pris en charge par tous les principaux navigateurs actuels (sauf IE) et caniuse.com affiche un soutien de 87% sur la part de marché mondiale. Encore une fois pas de support dans IE ou Edge non chromé.

Nicolai Schmid
la source
êtes-vous sûr de votre modification? la proposition montre un exemple avec un chemin variable: github.com/tc39/proposal-dynamic-import#example
phil294
@Blauhirn j'étais, mais votre lien montre clairement que c'est une possibilité. Bien que je n'ai aucune idée de la façon dont Webpack, par exemple, résoudrait ces importations
Nicolai Schmid
corrigez-moi si je me trompe, mais webpack ne les traite pas, n'est-ce pas? Je pensais que c'était le but des importations dynamiques qu'elles s'exécutent "telles quelles" dans le navigateur.
phil294
Oui, vous pouvez les exécuter dans le navigateur tels quels. Mais webpack utilise automatiquement les importations pour diviser votre application en plusieurs ensembles pour différentes parties de votre application, par exemple pour les itinéraires. Je les utilise tout le temps et ils sont vraiment utiles. Et en ce qui concerne le «traitement»; webpack transmettra les importations au babel, qui remplira automatiquement la fonction pour les navigateurs plus anciens.
Nicolai Schmid
Pour être clair: l' importation dynamique () va travailler avec des variables et n'est pas nécessaire d'être statiquement analysable (qui est tout le point de « dynamique », sûrement?). Voyez ma réponse.
Velojet
6

Je comprends la question spécifiquement posée pour ES6 importdans Node.js, mais ce qui suit pourrait aider les autres à rechercher une solution plus générique:

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

Notez que si vous importez un module ES6 et que vous devez accéder à l' defaultexportation, vous devrez utiliser l'un des éléments suivants:

let variableName = "es6.js";

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

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

Vous pouvez également utiliser la déstructuration avec cette approche qui peut ajouter plus de familiarité avec la syntaxe de vos autres importations:

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

Malheureusement, si vous souhaitez accéder defaultainsi que la déstructuration, vous devrez effectuer cette opération en plusieurs étapes:

// 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
la source
4

vous pouvez utiliser la notation non ES6 pour ce faire. C'est ce qui a fonctionné pour moi:

let myModule = null;
if (needsToLoadModule) {
  myModule = require('my-module').default;
}
Mlevanon
la source
3

J'aime moins cette syntaxe, mais ça marche:
au lieu d'écrire

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

utilisez cette syntaxe:

let memberName = require("path" + "fileName");
Gil Epshtain
la source
1
@UlysseBN Différent dans le mauvais sens? Ou une manière qui n'a pas vraiment d'importance?
Sam le
@Jacob, ils sont vraiment complètement différents, alors oui, cela peut avoir de l'importance en fonction de ce que vous faites. La première syntaxe est évaluée statiquement, tandis que la seconde est évaluée dynamiquement. Ainsi, par exemple, si vous utilisez webpack, ce ne sera pas pour effectuer correctement le tremblement d'arbre avec le second. Il existe de nombreuses autres différences, je vous suggère de lire la doc et de voir laquelle est la plus appropriée pour vous!
Ulysse BN
@Jacob - Peu importe (dans la plupart des cas). require()est une méthode Node.JS pour charger des fichiers, qui est la première version. importdéclaration est la version la plus récente, qui fait maintenant partie de la syntaxe de la langue officielle. Cependant, dans de nombreux cas, le navigateur utilisera le précédent (derrière la science). L'instruction require encaissera également vos fichiers, donc si un fichier est chargé une deuxième fois, il sera chargé à partir de la mémoire (meilleures performances). La méthode d'importation a ses propres avantages - si vous utilisez WebPack. alors webpack peut supprimer les références mortes (ces scripts ne seront pas téléchargés sur le client).
Gil Epshtain
1

L'importation dynamique () (disponible dans Chrome 63+) fera votre travail. Voici comment:

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

Je le ferais comme ça

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
avril
la source
0

./utils/test.js

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

appel depuis un fichier

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Andres Munoz
la source