Come posso importare condizionalmente un modulo ES6?


194

Devo fare qualcosa del tipo:

if (condition) {
    import something from 'something';
}
// ...
if (something) {
    something.doStuff();
}

Il codice sopra non viene compilato; getta SyntaxError: ... 'import' and 'export' may only appear at the top level.

Ho provato a usare System.importcome mostrato qui , ma non so da dove Systemviene. È una proposta ES6 che non è stata accettata? Il link all '"API programmatica" di quell'articolo mi porta a una pagina di documenti obsoleti .


Basta importarlo normalmente. Il tuo modulo ne ha bisogno a prescindere.
Andy,

Non vedo davvero alcun motivo per cui non dovresti importare a prescindere dalla condizione. Non è che ci sia una sorta di sovraccarico. In alcuni scenari è necessario il file, quindi non è come se ci fosse mai un caso in cui può essere completamente ignorato. In tal caso, importalo incondizionatamente.
Grazie

8
Il mio caso d'uso: voglio semplificare la dipendenza opzionale. Se il dep non è necessario, l'utente lo rimuove da package.json; my gulpfileverifica quindi l'esistenza di tale dipendenza prima di eseguire alcuni passaggi di compilazione.
ericsoco

1
Un altro caso d'uso: a scopo di test. Sto usando webpacke babelper traspilare es6 in es5. Progetti simili webpack-rewiree simili non sono di aiuto qui - github.com/jhnns/rewire-webpack/issues/12 . Un modo per impostare il doppio del test O per rimuovere dipendenze problematiche potrebbe essere l'importazione condizionale.
Amio.io,

3
+1. Essere in grado di utilizzare un modulo in più ambienti in cui le dipendenze possono o meno funzionare è fondamentale, in particolare quando i moduli possono fare riferimento a dipendenze che funzionerebbero solo nel browser (ad es. Dove webpackviene utilizzato per convertire fogli di stile in moduli che inseriscono gli stili pertinenti nel DOM quando vengono importati) ma il modulo deve essere eseguito anche al di fuori del browser (ad es. Per test unitari).
Jules il

Risposte:


146

Abbiamo una proposta di importazione dinamica ora con ECMA. Questo è nella fase 3. Questo è disponibile anche come preimpostazione babele .

Di seguito è riportato un modo per eseguire il rendering condizionale secondo il tuo caso.

if (condition) {
    import('something')
    .then((something) => {
       console.log(something.something);
    });
}

Questo sostanzialmente restituisce una promessa. La risoluzione della promessa dovrebbe avere il modulo. La proposta ha anche altre funzionalità come le importazioni multiple dinamiche, le importazioni predefinite, l'importazione di file js ecc. Puoi trovare maggiori informazioni sulle importazioni dinamiche qui .


13
Finalmente una vera risposta ES6! Grazie @thecodejack. In realtà allo stadio 3 al momento della stesura di questo articolo, secondo l'articolo in questo momento.
ericsoco,

5
o se hai appena nominato le esportazioni puoi distruggere:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
ivn

4
su Firefox e durante l'esecuzione npm run buildottengo ancora l'errore:SyntaxError: ... 'import' and 'export' may only appear at the top level
ste

1
@stackjlei: questa funzione non fa ancora parte dello standard JavaScript, è solo una proposta della fase 3! Tuttavia, è già implementato in molti browser più recenti, vedi caniuse.com/#feat=es6-module-dynamic-import .
Konrad Höffner,

1
Quella funzione di importazione dinamica condizionale non ha la capacità di importare solo elementi particolari che ha "l'importazione di X da Y". In effetti quell'abilità a grana fine potrebbe essere ancora più importante nel caricamento dinamico (al contrario del raggruppamento preelaborato)
Craig Hicks,

102

Se lo desideri, puoi utilizzare Richiedi. Questo è un modo per avere una dichiarazione obbligatoria condizionale.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}

1
Penso che qualcosa e altre variabili vengano declassate usando const che è a blocchi di blocco, quindi la seconda condizione se genererà qualcosa che non è definito
Mohammed Essehemy

Sarebbe meglio usare let e dichiarare le due variabili al di fuori del blocco invece di usare 'var' ed evitare del tutto l'ambito del blocco.
Vorcan,

Il sollevamento influisce su qualcosa in questo caso? Ho riscontrato alcuni problemi in cui il sollevamento ha significato che ho importato inaspettatamente una libreria quando seguivo un modello vicino a questo se la memoria serve.
Thomas,

11
Va sottolineato che require()non fa parte del JavaScript standard: è una funzione integrata in Node.js, quindi utile solo in quell'ambiente. L'OP non fornisce indicazioni su come lavorare con Node.js.
Velojet,

56

Non puoi importare in modo condizionale, ma puoi fare il contrario: esportare qualcosa in modo condizionale. Dipende dal tuo caso d'uso, quindi questo lavoro potrebbe non essere adatto a te.

Tu puoi fare:

api.js

import mockAPI from './mockAPI'
import realAPI from './realAPI'

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

Lo uso per deridere librerie di analisi come mixpanel, ecc ... perché al momento non posso avere più build o il nostro frontend. Non il più elegante, ma funziona. Ho solo qualche 'if' qua e là a seconda dell'ambiente perché nel caso del mixpanel, ha bisogno di inizializzazione.


40
Questa soluzione causa il caricamento di moduli indesiderati, quindi non una soluzione ottimale, credo.
Ismailarilik,

5
Come indicato nella risposta, questa è una soluzione. A quel tempo, semplicemente non c'era soluzione. Le importazioni di ES6 non sono dinamiche, questo è di progettazione. La proposta della funzione di importazione dinamica ES6, descritta nella risposta attualmente accettata, può farlo. JS si sta evolvendo :)
Kev,

9

Sembra che la risposta sia che, per ora, non puoi.

http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api

Penso che l'intenzione sia quella di abilitare il più possibile l'analisi statica e che i moduli importati condizionatamente lo rompano. Vale anche la pena ricordare che sto usando Babel e suppongo che Systemnon sia supportato da Babel perché l'API del caricatore di moduli non è diventata uno standard ES6.


@FelixKling crea una sua risposta e la accetterò felicemente!
ericsoco,

4

require()è un modo per importare alcuni moduli in fase di esecuzione e si qualifica ugualmente per l'analisi statica come importse utilizzato con percorsi letterali di stringa. Questo è richiesto dal bundler per selezionare le dipendenze per il bundle.

const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;

Per la risoluzione dinamica dei moduli con supporto completo per l'analisi statica, i primi moduli indice in un indicizzatore (index.js) e importare l'indicizzatore nel modulo host.

// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';

// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);

7
Va sottolineato che require()non fa parte del JavaScript standard: è una funzione integrata in Node.js, quindi utile solo in quell'ambiente. L'OP non fornisce indicazioni su come lavorare con Node.js.
Velojet,

2

Differenza importante se si utilizza la modalità Webpack di importazione dinamica eager:

if (normalCondition) {
  // this will be included to bundle, whether you use it or not
  import(...);
}

if (process.env.SOMETHING === 'true') {
  // this will not be included to bundle, if SOMETHING is not 'true'
  import(...);
}

Ma importrestituisce una promessa.
Newguy

0

oscurandolo in una valutazione ha funzionato per me, nascondendolo dall'analizzatore statico ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}

3
Qualcuno può spiegare perché questa risposta è stata sottoposta a downgrade? C'è qualche vero svantaggio o è stata solo una reazione negativa automatica alla parola chiave malvagia 'eval'?
Yuri Gor,

3
Downvote automatico per l'utilizzo della terribile parola chiave eval. Stai lontano.
Tormod Haugene,

1
Puoi spiegare cosa c'è di veramente sbagliato nell'uso di evalqui, @TormodHaugene?
Adam Barnes,

MDN riassume alcune ragioni per cui evalnon dovrebbe essere usato . In generale: se trovi la necessità di usare eval, probabilmente stai sbagliando e dovresti fare un passo indietro per considerare le tue alternative. Probabilmente ci sono alcuni scenari in cui l'utilizzo evalè corretto, ma molto probabilmente non hai riscontrato una di queste situazioni.
Tormod Haugene,

5
Va sottolineato che require()non fa parte del JavaScript standard: è una funzione integrata in Node.js, quindi utile solo in quell'ambiente. L'OP non fornisce indicazioni su come lavorare con Node.js.
Velojet,

0

Sono stato in grado di ottenere questo risultato usando una funzione immediatamente invocata e richiedendo una dichiarazione.

const something = (() => (
  condition ? require('something') : null
))();

if(something) {
  something.doStuff();
}

5
Va sottolineato che require()non fa parte del JavaScript standard: è una funzione integrata in Node.js, quindi utile solo in quell'ambiente. L'OP non fornisce indicazioni su come lavorare con Node.js.
Velojet,

0

Le importazioni condizionali potrebbero anche essere realizzate con un ternario e require():

const logger = DEBUG ? require('dev-logger') : require('logger');

Questo esempio è stato tratto dai documenti di ES Lint per i requisiti globali: https://eslint.org/docs/rules/global-require


5
Va sottolineato che require()non fa parte del JavaScript standard: è una funzione integrata in Node.js, quindi utile solo in quell'ambiente. L'OP non fornisce indicazioni su come lavorare con Node.js.
Velojet,


0

No, non puoi!

Tuttavia, essersi imbattuto in questo problema dovrebbe farti ripensare su come organizzare il codice.

Prima dei moduli ES6, avevamo moduli CommonJS che utilizzavano la sintassi request (). Questi moduli erano "dinamici", il che significa che potevamo importare nuovi moduli in base alle condizioni nel nostro codice. - fonte: https://bitsofco.de/what-is-tree-shaking/

Immagino che uno dei motivi per cui hanno abbandonato il supporto su ES6 sia il fatto che compilarlo sarebbe molto difficile o impossibile.

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.