Nome di importazione della variabile ES6 in node.js?


108

è possibile importare qualcosa nel modulo fornendo il nome della variabile durante l'utilizzo dell'importazione ES6?

Cioè, voglio importare un modulo in fase di esecuzione a seconda dei valori forniti in una configurazione:

import something from './utils/' + variableName;

1
@Bigood sì, il compilatore vomita e Webstorm mostra anche un errore
Vytautas Butkus

Risposte:


67

Non con l' importaffermazione.importe exportsono definiti in modo tale da essere analizzabili staticamente, quindi non possono dipendere dalle informazioni di runtime.

Stai cercando l' API del caricatore (polyfill) , ma non sono un po 'chiaro sullo stato delle specifiche:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});

3
devo "richiedere" il sistema? Non è di portata globale .. Ps Sto usando babel js
Vytautas Butkus

Non è ancora implementato da nessuna parte AFAIK. Devi usare il polyfill dal link.
Felix Kling

1
Appena controllato, funziona (prova a caricare il file) ma poi incasina i percorsi per altri moduli ... Peccato che non sia supportato nativamente ..
Vytautas Butkus

3
È ancora un problema? Devo caricare dinamicamente i moduli ES6 ma non ho avuto successo ..
calbertts

26

Oltre alla risposta di Felix , noterò esplicitamente che questo non è attualmente consentito dalla grammatica ECMAScript 6 :

ImportDeclaration :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • da ModuleSpecifier

ModuleSpecifier :

  • letterale stringa

Un ModuleSpecifier può essere solo un StringLiteral , non qualsiasi altro tipo di espressione come AdditiveExpression .


2
È un peccato che questo non sia stato esteso per includere const string literals. Sono staticamente analizzabili, no? Renderebbe possibile il riutilizzo della posizione di una dipendenza. (es. importare un modello e avere sia il modello che la posizione del modello disponibili).
nicodemus13

26

Sebbene questa non sia effettivamente un'importazione dinamica (ad es. Nella mia circostanza, tutti i file che sto importando di seguito verranno importati e raggruppati da webpack, non selezionato in fase di runtime), un modello che ho utilizzato che può aiutare in alcune circostanze è :

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

const templates = {
  Template1,
  Template2
};

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

o in alternativa:

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

Non penso di poter tornare a un valore predefinito con la stessa facilità require(), il che genera un errore se provo a importare un percorso del modello costruito che non esiste.

Buoni esempi e confronti tra require e import possono essere trovati qui: http://www.2ality.com/2014/09/es6-modules-final.html

Eccellente documentazione sulla riesportazione da @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Sono interessato a ricevere feedback su questo approccio :)


Upvote. Ho usato l'approccio "o in alternativa". Ha funzionato come un fascino per la mia soluzione di localizzazione personalizzata.
groundh0g


1
Avrei dovuto pensarci. Ottima soluzione, non importa come stai importando le cose (e anche se non stai importando nulla). Hai un elenco di elementi di cui vuoi ottenere il nome o per nome in seguito? Inseriscili in un letterale oggetto invece che in un valore letterale array e lascia che la sintassi dell'oggetto si occupi di nominarli in base al nome della loro costante / variabile locale. Se ti servono di nuovo come elenco, fallo Object.values(templates).
Andrew Koster,

15

C'è una nuova specifica chiamata importazione dinamica per i moduli ES. Fondamentalmente, chiami import('./path/file.js')e sei a posto. La funzione restituisce una promessa, che si risolve con il modulo se l'importazione è andata a buon fine.

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

Casi d'uso

I casi d'uso includono l' importazione di componenti basati su route per React, Vue ecc. E la possibilità di caricare i moduli in modo lento , una volta che sono richiesti durante il runtime.

Ulteriori informazioni

Ecco una spiegazione su Google Developers .

Compatibilità del browser (aprile 2020)

Secondo MDN è supportato da tutti i principali browser attuali (eccetto IE) e caniuse.com mostra il supporto dell'87% in tutta la quota di mercato globale. Anche in questo caso nessun supporto in IE o Edge non cromato.


sei sicuro della tua modifica? la proposta mostra un esempio con un percorso variabile: github.com/tc39/proposal-dynamic-import#example
phil294

@ Blauhirn lo ero, ma il tuo link mostra chiaramente che è una possibilità. Anche se non ho idea di come webpack, ad esempio, risolverebbe queste importazioni
Nicolai Schmid

correggimi se sbaglio, ma webpack non li elabora, vero? Ho pensato che fosse il punto delle importazioni dinamiche che vengono eseguite "così come sono" nel browser.
phil294

Sì, puoi eseguirli nel browser così come sono. Ma webpack utilizza automaticamente le importazioni per suddividere la tua app in più bundle per parti diverse della tua app, ad esempio per i percorsi. Li uso sempre e sono davvero utili. E per quanto riguarda la "lavorazione"; webpack passerà le importazioni al babel, che riempirà la funzionalità dei browser meno recenti.
Nicolai Schmid

Per essere chiari: import dinamico () sarà lavorare con le variabili e non è necessario che sia staticamente analizzabile (che è il punto centrale di 'dinamica', no?). Vedi la mia risposta.
Velojet

6

Comprendo la domanda specificamente richiesta per ES6 importin Node.js, ma quanto segue potrebbe aiutare gli altri alla ricerca di una soluzione più generica:

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

Nota se stai importando un modulo ES6 e defaultdevi accedere all'esportazione, dovrai utilizzare uno dei seguenti:

let variableName = "es6.js";

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

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

Puoi anche utilizzare la destrutturazione con questo approccio che può aggiungere più familiarità alla sintassi con le altre importazioni:

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

Sfortunatamente, se vuoi accedere defaultoltre alla destrutturazione, dovrai eseguire questa operazione in più passaggi:

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

4

puoi usare la notazione non ES6 per farlo. Questo è ciò che ha funzionato per me:

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

3

Mi piace meno questa sintassi, ma funziona:
invece di scrivere

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

usa questa sintassi:

let memberName = require("path" + "fileName");

1
@UlysseBN Diverso in un brutto modo? O un modo che non ha molta importanza?
Sam

@Jacob sono davvero completamente diversi, quindi sì, potrebbe importare a seconda di quello che stai facendo. La prima sintassi viene valutata staticamente, mentre la seconda viene valutata dinamicamente. Quindi, ad esempio, se stai usando webpack, non sarà un eseguire correttamente lo scuotimento degli alberi con il secondo. Ci sono molte altre differenze, ti consiglio di leggere il documento e vedere quale è più adatto a te!
Ulysse BN

@ Jacob - Non importa (nella maggior parte dei casi). require()è un metodo Node.JS per il caricamento dei file, che è la prima versione. importè la versione più recente, che ora fa parte della sintassi del linguaggio ufficiale. Tuttavia in molti casi il browser utilizzerà quello precedente (dietro la scienza). L'istruzione require incasserà anche i tuoi file, quindi se un file viene caricato la seconda volta verrà caricato dalla memoria (prestazioni migliori). Il modo di importazione ha i suoi vantaggi, se utilizzi WebPack. quindi webpack può rimuovere i riferimenti morti (questi script non verranno scaricati sul client).
Gil Epshtain

1

L'importazione dinamica () (disponibile in Chrome 63+) farà il tuo lavoro. Ecco come:

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

0

Lo farei così

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

0

./utils/test.js

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

chiamata da file

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.