Jak mogę warunkowo zaimportować moduł ES6?


194

Muszę zrobić coś takiego:

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

Powyższy kod się nie kompiluje; rzucaSyntaxError: ... 'import' and 'export' may only appear at the top level .

Próbowałem używać, System.importjak pokazano tutaj , ale nie wiem, skąd Systempochodzi. Czy to propozycja ES6, która nie została zaakceptowana? Link do „programowego interfejsu API” z tego artykułu zrzuca mnie na przestarzałą stronę z dokumentami .


Po prostu zaimportuj go normalnie. Twój moduł tego potrzebuje.
Andy

Naprawdę nie widzę żadnego powodu, dla którego po prostu nie importowałbyś, niezależnie od warunku. To nie jest tak, że istnieje jakiś narzut. W niektórych scenariuszach potrzebujesz pliku, więc nie jest tak, że istnieje przypadek, w którym można go całkowicie pominąć. W takim przypadku po prostu zaimportuj go bezwarunkowo.
Dziękuję

8
Mój przypadek użycia: Chcę ułatwić opcjonalną zależność. Jeśli dep nie jest potrzebny, użytkownik usuwa go z package.json; moi gulpfilenastępnie sprawdza, czy istnieje zależność, że przed wykonaniem pewne kroki kompilacji.
ericsoco

1
Kolejny przypadek użycia: do celów testowych. Używam webpacki babeldo transponowania es6 na es5. Projekty takie jak webpack-rewirei podobne nie mają tutaj pomóc - github.com/jhnns/rewire-webpack/issues/12 . Jednym ze sposobów ustawienia podwójnego testu LUB w celu usunięcia problematycznych zależności może być import warunkowy.
Amio.io,

3
+1. Możliwość korzystania z modułu w wielu środowiskach, w których zależności mogą, ale nie muszą działać, ma kluczowe znaczenie, szczególnie gdy moduły mogą odnosić się do zależności, które działałyby tylko w przeglądarce (np. W przypadku webpackkonwersji arkuszy stylów na moduły wstawiające odpowiednie style do DOM po zaimportowaniu), ale moduł musi również działać poza przeglądarką (np. W celu testowania jednostkowego).
Jules

Odpowiedzi:


144

Mamy teraz dynamiczną propozycję importu z ECMA. Jest to etap 3. Jest to również dostępne jako preset babel .

Poniżej przedstawiono sposób renderowania warunkowego zgodnie z Twoim przypadkiem.

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

To w zasadzie zwraca obietnicę. Oczekuje się, że rozwiązanie obietnicy będzie mieć moduł. Propozycja ma również inne funkcje, takie jak import dynamiczny, import domyślny, import pliku js itp. Więcej informacji na temat importu dynamicznego można znaleźć tutaj .


13
Wreszcie prawdziwa odpowiedź ES6! Dzięki @thecodejack. Właściwie na etapie 3 w chwili pisania tego tekstu, zgodnie z tym artykułem teraz.
ericsoco,

5
lub jeśli właśnie if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
nadałeś

4
w przeglądarce Firefox i podczas działania npm run buildnadal pojawia się błąd:SyntaxError: ... 'import' and 'export' may only appear at the top level
ste

1
@stackjlei: Ta funkcja nie jest jeszcze częścią standardu JavaScript, to tylko propozycja trzeciego etapu! Jednak jest już zaimplementowany w wielu nowszych przeglądarkach, patrz caniuse.com/#feat=es6-module-dynamic-import .
Konrad Höffner,

1
Ta funkcja dynamicznego importu warunkowego nie ma drobnoziarnistej możliwości importowania tylko określonych elementów, które ma „import X z Y”. W rzeczywistości ta drobnoziarnista zdolność może być nawet ważniejsza w obciążeniu dynamicznym (w przeciwieństwie do łączenia w ramach procesu wstępnego)
Craig Hicks

102

Jeśli chcesz, możesz użyć wymagania. Jest to sposób na warunkowe żądanie warunkowe.

let something = null;
let other = null;

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

1
Myślę, że coś i inne zmienne są declsred użyciu const który blok scoped, więc jeśli drugi warunek rzuci, że coś nie jest zdefiniowane
Mohammed Essehemy

Lepiej byłoby użyć let i zadeklarować dwie zmienne poza blokiem zamiast używać „var” i całkowicie unikać zakresu bloku.
Vorcan

Czy podnoszenie wpływa na cokolwiek w tym przypadku? Wpadłem na pewne problemy, w których podnoszenie oznaczało, że niespodziewanie zaimportowałem bibliotekę, podążając za wzorem zbliżonym do tego, jeśli pamięć służy.
Thomas

11
Należy zauważyć, że require()nie jest to część standardowego JavaScript - jest to wbudowana funkcja w Node.js, więc przydatna tylko w tym środowisku. OP nie wskazuje na pracę z Node.js.
Velojet

55

Nie możesz importować warunkowo, ale możesz zrobić odwrotnie: eksportuj coś warunkowo. To zależy od przypadku użycia, więc obejście tego problemu może nie być dla Ciebie.

Możesz to zrobić:

api.js

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

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

Używam tego, by wyśmiewać biblioteki analityczne, takie jak mixpanel itp., Ponieważ nie mogę obecnie mieć wielu kompilacji ani naszego frontendu. Nie najbardziej elegancki, ale działa. Mam tylko kilka „jeśli” tu i tam, w zależności od środowiska, ponieważ w przypadku mixpanelu wymaga inicjalizacji.


40
To rozwiązanie powoduje ładowanie niechcianych modułów, więc myślę, że nie jest to optymalne rozwiązanie.
ismailarilik

5
Jak stwierdzono w odpowiedzi, jest to obejście. W tym czasie po prostu nie było rozwiązania. Import ES6 nie jest dynamiczny, jest to zgodne z projektem. Może to zrobić propozycja funkcji importu dynamicznego ES6, która jest opisana w obecnie akceptowanej odpowiedzi. JS ewoluuje :)
Kev

9

Wygląda na to, że odpowiedź jest taka, że ​​na razie nie możesz.

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

Myślę, że naszym celem jest możliwie jak największa analiza statyczna, a importowane warunkowo moduły to psują. Warto również wspomnieć - używam Babel i zgaduję, że Systemnie jest obsługiwany przez Babel, ponieważ interfejs API modułu ładującego nie stał się standardem ES6.


@FelixKling stwórz własną odpowiedź i chętnie ją przyjmę!
ericsoco

4

require()jest sposobem na zaimportowanie jakiegoś modułu w czasie wykonywania i w równym stopniu kwalifikuje się do analizy statycznej, jak w importprzypadku użycia ze ścieżkami literału łańcucha. Jest to wymagane przez pakiet do wybrania zależności dla pakietu.

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

Aby uzyskać dynamiczne rozwiązanie modułu z pełną obsługą analizy statycznej, najpierw moduły indeksujące w module indeksującym (index.js) i importuj moduł indeksujący w module hosta.

// 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
Należy zauważyć, że require()nie jest to część standardowego JavaScript - jest to wbudowana funkcja w Node.js, więc przydatna tylko w tym środowisku. OP nie wskazuje na pracę z Node.js.
Velojet

2

Ważna różnica w przypadku korzystania z trybu importu dynamicznego Webpack 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(...);
}

Ale importzwraca obietnicę.
newguy

0

przesłanianie go w eval zadziałało dla mnie, ukrywając go przed analizatorem statycznym ...

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

3
Czy ktoś może wyjaśnić, dlaczego ta odpowiedź została odrzucona? Czy są jakieś wady, czy była to tylko automatyczna negatywna reakcja na złe słowo kluczowe „eval”?
Yuri Gor

3
Automatyczne głosowanie za używanie ohydnego słowa kluczowego eval. Nie zbliżaj się.
Tormod Haugene

1
Czy możesz wyjaśnić, co jest właściwie nie tak z użyciem evaltutaj @TormodHaugene?
Adam Barnes,

MDN podsumowuje kilka powodów, dla evalktórych nie należy go używać . Ogólnie: jeśli uważasz, że musisz użyć eval, prawdopodobnie robisz to źle i powinieneś cofnąć się, aby rozważyć alternatywne rozwiązania. Prawdopodobnie istnieją pewne scenariusze, w których użycie evaljest prawidłowe, ale najprawdopodobniej nie spotkałeś żadnej z tych sytuacji.
Tormod Haugene

5
Należy zauważyć, że require()nie jest to część standardowego JavaScript - jest to wbudowana funkcja w Node.js, więc przydatna tylko w tym środowisku. OP nie wskazuje na pracę z Node.js.
Velojet

0

Udało mi się to osiągnąć za pomocą natychmiastowo wywołanej funkcji i wymagają instrukcji.

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

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

5
Należy zauważyć, że require()nie jest to część standardowego JavaScript - jest to wbudowana funkcja w Node.js, więc przydatna tylko w tym środowisku. OP nie wskazuje na pracę z Node.js.
Velojet

0

Import warunkowy można również osiągnąć za pomocą trójki i require()s:

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

Ten przykład został zaczerpnięty z dokumentów globalnych wymagających ES Lint: https://eslint.org/docs/rules/global-require


5
Należy zauważyć, że require()nie jest to część standardowego JavaScript - jest to wbudowana funkcja w Node.js, więc przydatna tylko w tym środowisku. OP nie wskazuje na pracę z Node.js.
Velojet


0

Nie, nie możesz!

Jednak wpadnięcie na ten problem powinno sprawić, że ponownie przemyślisz sposób organizacji kodu.

Przed modułami ES6 mieliśmy moduły CommonJS, które używały składni wymaganej (). Te moduły były „dynamiczne”, co oznacza, że ​​mogliśmy importować nowe moduły w oparciu o warunki w naszym kodzie. - źródło: https://bitsofco.de/what-is-tree-shaking/

Wydaje mi się, że jednym z powodów, dla których porzucili tę obsługę ES6, jest fakt, że skompilowanie jej byłoby bardzo trudne lub niemożliwe.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.