Wie kann ich ein ES6-Modul bedingt importieren?

192

Ich muss so etwas tun wie:

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

Der obige Code wird nicht kompiliert. es wirft SyntaxError: ... 'import' and 'export' may only appear at the top level.

Ich habe versucht, System.importwie hier gezeigt zu verwenden , aber ich weiß nicht, woher es Systemkommt. Ist es ein ES6-Vorschlag, der nicht angenommen wurde? Der Link zu "Programmatic API" aus diesem Artikel führt mich zu einer veralteten Dokumentenseite .

ericsoco
quelle
Importieren Sie es einfach normal. Ihr Modul benötigt es trotzdem.
Andy
Ich sehe keinen Grund, warum Sie nicht einfach importieren würden, unabhängig von der Bedingung. Es ist nicht so, als gäbe es einen Overhead. In einigen Szenarien benötigen Sie die Datei, sodass es nie einen Fall gibt, in dem sie vollständig übersprungen werden kann. In diesem Fall importieren Sie es einfach bedingungslos.
Vielen Dank, dass Sie
8
Mein Anwendungsfall: Ich möchte es einfach machen, eine optionale Abhängigkeit zu haben. Wenn die Dep nicht benötigt wird, entfernt der Benutzer sie aus package.json. my gulpfileprüft dann, ob diese Abhängigkeit besteht, bevor einige Build-Schritte ausgeführt werden.
Ericsoco
1
Ein weiterer Anwendungsfall: zu Testzwecken. Ich benutze webpackund babeltranspiliere es6 zu es5. Projekte wie webpack-rewireund ähnliche sollen hier nicht helfen - github.com/jhnns/rewire-webpack/issues/12 . Eine Möglichkeit, die Testdoppelwerte festzulegen ODER problematische Abhängigkeiten zu entfernen, könnte der bedingte Import sein.
Amio.io
3
+1. Die Verwendung eines Moduls in mehreren Umgebungen, in denen Abhängigkeiten möglicherweise funktionieren oder nicht, ist von entscheidender Bedeutung, insbesondere wenn Module auf Abhängigkeiten verweisen, die nur im Browser funktionieren würden (z. B. wo webpackStylesheets in Module konvertiert werden, die die relevanten Stile in die einfügen DOM beim Importieren), aber das Modul muss auch außerhalb des Browsers ausgeführt werden (z. B. zum Testen von Einheiten).
Jules

Antworten:

142

Wir haben jetzt einen dynamischen Importvorschlag bei ECMA. Dies ist in Stufe 3. Dies ist auch als Babel-Preset verfügbar .

Im Folgenden finden Sie eine Möglichkeit zum bedingten Rendern gemäß Ihrem Fall.

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

Dies gibt im Grunde ein Versprechen zurück. Die Auflösung des Versprechens wird voraussichtlich das Modul haben. Der Vorschlag enthält auch andere Funktionen wie mehrere dynamische Importe, Standardimporte, Import von JS-Dateien usw. Weitere Informationen zu dynamischen Importen finden Sie hier .

thecodejack
quelle
13
Endlich eine echte ES6-Antwort! Danke @thecodejack. Eigentlich in Stufe 3 zum Zeitpunkt dieses Schreibens, laut diesem Artikel jetzt.
Ericsoco
5
oder wenn Sie gerade Exporte benannt haben, können Sie zerstören:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
ivn
4
auf Firefox und während des Laufens npm run builderhalte ich immer noch den Fehler:SyntaxError: ... 'import' and 'export' may only appear at the top level
Ste
Dies schlägt beim Testen fehl. Hat jemand Ideen?
Stackjlei
1
Diese bedingte dynamische Importfunktion verfügt nicht über die feinkörnige Fähigkeit, nur bestimmte Elemente zu importieren, über die "X aus Y importieren" verfügt. In der Tat könnte die feinkörnige Fähigkeit beim dynamischen Laden noch wichtiger sein (im Gegensatz zur Bündelung vor dem Prozess)
Craig Hicks
101

Wenn Sie möchten, können Sie erfordern verwenden. Dies ist eine Möglichkeit, eine bedingte require-Anweisung zu erhalten.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}
BaptWaels
quelle
1
Ich denke, dass etwas und andere Variablen mit const deklariert werden, das blockumfangig ist, so dass die zweite if-Bedingung auslöst, dass etwas nicht definiert ist
Mohammed Essehemy
Es wäre besser, let und deklarieren Sie die beiden Variablen außerhalb des Blocks, anstatt 'var' zu verwenden und den Blockbereich insgesamt zu vermeiden.
Vorcan
Beeinflusst das Heben in diesem Fall etwas? Ich bin auf einige Probleme gestoßen, bei denen das Heben dazu geführt hat, dass ich unerwartet eine Bibliothek importiert habe, wenn ich einem ähnlichen Muster folge, wenn Speicher zur Verfügung steht.
Thomas
11
Es muss darauf hingewiesen werden, dass dies require()nicht Teil von Standard-JavaScript ist - es ist eine in Node.js integrierte Funktion, die nur in dieser Umgebung nützlich ist. Das OP gibt keinen Hinweis auf die Arbeit mit Node.js.
Velojet
53

Sie können nicht bedingt importieren, aber Sie können das Gegenteil tun: etwas bedingt exportieren. Dies hängt von Ihrem Anwendungsfall ab, sodass diese Problemumgehung möglicherweise nicht für Sie geeignet ist.

Du kannst tun:

api.js

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

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

Ich benutze das, um Analytics-Bibliotheken wie Mixpanel usw. zu verspotten, weil ich derzeit nicht mehrere Builds oder unser Frontend haben kann. Nicht das eleganteste, funktioniert aber. Ich habe hier und da nur ein paar "Wenn", abhängig von der Umgebung, da im Fall von Mixpanel eine Initialisierung erforderlich ist.

Kev
quelle
39
Diese Lösung führt dazu, dass unerwünschte Module geladen werden, also keine optimale Lösung, denke ich.
ismailarilik
5
Wie in der Antwort angegeben, handelt es sich hierbei um eine Problemumgehung. Zu dieser Zeit gab es einfach keine Lösung. ES6-Importe sind nicht dynamisch, dies ist beabsichtigt. Der Vorschlag für eine dynamische ES6-Importfunktion, der in der aktuell akzeptierten Antwort beschrieben ist, kann dies tun. JS entwickelt sich weiter :)
Kev
9

Sieht so aus, als ob die Antwort lautet, dass Sie ab sofort nicht mehr können.

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

Ich denke, die Absicht ist es, die statische Analyse so weit wie möglich zu ermöglichen, und bedingt importierte Module brechen dies. Erwähnenswert ist auch, dass ich Babel verwende und ich vermute, dass Systemdies von Babel nicht unterstützt wird, da die Modullader-API nicht zum ES6-Standard wurde.

ericsoco
quelle
@FelixKling mache dies zu seiner eigenen Antwort und ich werde es gerne akzeptieren!
Ericsoco
4

require()ist eine Möglichkeit, ein Modul zur Laufzeit zu importieren, und es eignet sich gleichermaßen für statische Analysen, wie importwenn es mit String-Literalpfaden verwendet wird. Dies wird vom Bundler benötigt, um Abhängigkeiten für das Bundle auszuwählen.

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

Für eine dynamische Modulauflösung mit vollständiger Unterstützung der statischen Analyse müssen Sie zuerst die Indexmodule in einem Indexer (index.js) und den Indexer in das Hostmodul importieren.

// 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]);
Shoaib Nawaz
quelle
7
Es muss darauf hingewiesen werden, dass dies require()nicht Teil von Standard-JavaScript ist - es ist eine in Node.js integrierte Funktion, die nur in dieser Umgebung nützlich ist. Das OP gibt keinen Hinweis auf die Arbeit mit Node.js.
Velojet
2

Wichtiger Unterschied, wenn Sie den dynamischen Import-Webpack-Modus verwenden 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(...);
}
Solo
quelle
Aber importgibt ein Versprechen zurück.
Newguy
0

es in einer Auswertung zu verdecken, hat für mich funktioniert und es vor dem statischen Analysator versteckt ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}
Chris Marstall
quelle
3
Kann jemand erklären, warum diese Antwort abgelehnt wurde? Gibt es echte Nachteile oder war es nur eine automatische negative Reaktion auf das böse Schlüsselwort "eval"?
Yuri Gor
3
Automatische Abwertung für die Verwendung des abscheulichen Schlüsselworts eval. Bleib weg.
Tormod Haugene
1
Können Sie erklären, was mit der Verwendung von eval@TormodHaugene hier eigentlich falsch ist ?
Adam Barnes
MDN fasst einige Gründe zusammen, warum evales nicht verwendet werden sollte . Im Allgemeinen: Wenn Sie die Notwendigkeit einer Verwendung feststellen eval, machen Sie es wahrscheinlich falsch und sollten einen Schritt zurücktreten, um Ihre Alternativen in Betracht zu ziehen. Es gibt wahrscheinlich einige Szenarien, in denen die Verwendung evalkorrekt ist, aber Sie sind höchstwahrscheinlich nicht auf eine dieser Situationen gestoßen.
Tormod Haugene
5
Es muss darauf hingewiesen werden, dass dies require()nicht Teil von Standard-JavaScript ist - es ist eine in Node.js integrierte Funktion, die nur in dieser Umgebung nützlich ist. Das OP gibt keinen Hinweis auf die Arbeit mit Node.js.
Velojet
0

Ich konnte dies mit einer sofort aufgerufenen Funktion erreichen und eine Anweisung benötigen.

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

if(something) {
  something.doStuff();
}
bradley2w1dl
quelle
5
Es muss darauf hingewiesen werden, dass dies require()nicht Teil von Standard-JavaScript ist - es ist eine in Node.js integrierte Funktion, die nur in dieser Umgebung nützlich ist. Das OP gibt keinen Hinweis auf die Arbeit mit Node.js.
Velojet
0

Bedingte Importe könnten auch mit einem ternären und require()s erreicht werden:

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

Dieses Beispiel wurde aus den global erforderlichen ES Lint-Dokumenten entnommen: https://eslint.org/docs/rules/global-require

Elliot Ledger
quelle
5
Es muss darauf hingewiesen werden, dass dies require()nicht Teil von Standard-JavaScript ist - es ist eine in Node.js integrierte Funktion, die nur in dieser Umgebung nützlich ist. Das OP gibt keinen Hinweis auf die Arbeit mit Node.js.
Velojet
0

Nein, das kannst du nicht!

Wenn Sie jedoch auf dieses Problem gestoßen sind, sollten Sie überlegen, wie Sie Ihren Code organisieren.

Vor ES6-Modulen hatten wir CommonJS-Module, die die require () -Syntax verwendeten. Diese Module waren "dynamisch", was bedeutet, dass wir neue Module basierend auf den Bedingungen in unserem Code importieren konnten. - Quelle: https://bitsofco.de/what-is-tree-shaking/

Ich denke, einer der Gründe, warum sie diese Unterstützung ab ES6 eingestellt haben, ist die Tatsache, dass das Kompilieren sehr schwierig oder unmöglich wäre.

Aldee
quelle