ES6: Warunkowe i dynamiczne instrukcje importu

85

Warunkowy

Czy można mieć warunkowe instrukcje importu, jak poniżej?

if (foo === bar) {
    import Baz from './Baz';
}

Wypróbowałem powyższe, ale podczas kompilacji otrzymałem następujący błąd (z Babel).

'import' and 'export' may only appear at the top level

Dynamiczny

Czy można mieć dynamiczne instrukcje importu, jak poniżej?

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        import Baz from `./${foo}`;
    }
}

Powyższe powoduje ten sam błąd od Babel podczas kompilacji.

Czy to jest możliwe, czy czegoś mi brakuje?

Rozumowanie

Powodem, dla którego próbuję to zrobić, jest to, że mam wiele importów dla wielu „stron” i mają one podobny wzorzec. Chciałbym oczyścić swoją bazę kodu, importując te pliki z dynamiczną pętlą for.

Jeśli nie jest to możliwe, czy istnieje lepszy sposób obsługi dużej liczby importów w ES6?

Enijar
źródło
1
czy w takim przypadku nie można zastosować dziedziczenia? użyj superdo połączenia określonego.
Jai
Już używam dziedziczenia, ale te „strony” zawierają specyficzną logikę „strony”. Mam podstawową klasę „page”, która jest rozszerzana, ale to nie wystarczy, aby wyczyścić ogromną liczbę importów, które mam.
Enijar
1
@zerkms: Nie są wyciągane z bloków - to błędy składniowe.
Bergi
możliwy duplikat nazwy importu zmiennej ES6 w node.js ?
Bergi

Odpowiedzi:

54

Mamy teraz propozycję dynamicznego importu z ECMA. To jest na etapie 2. Jest to również dostępne jako ustawienie wstępne babel .

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

if (foo === bar) {
    import('./Baz')
    .then((Baz) => {
       console.log(Baz.Baz);
    });
}

To w zasadzie zwraca obietnicę. Rozwiązanie obietnicy ma mieć moduł. Propozycja zawiera również takie rzeczy, jak wielokrotne importowanie dynamiczne, import domyślny, import plików js itp. Więcej informacji o importowaniu dynamicznym można znaleźć tutaj .

thecodejack
źródło
3
To. Najlepszym rozwiązaniem jest dynamiczny import. Działają podobnie jak require (), z wyjątkiem tego, że dają raczej obietnicę niż moduł.
superluminarium
24

Nie możesz rozwiązywać swoich zależności dynamicznie, ponieważ importssą one przeznaczone do analizy statycznej. Jednak prawdopodobnie możesz użyć requiretutaj czegoś takiego, jak:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}
Jonathan Petitcolas
źródło
8
„ponieważ importy są przeznaczone do analizy statycznej” - to stwierdzenie jest niejasne. imports są przeznaczone do importu, a nie do analizy.
zerkms
12
@zerkms - myślę, że chodziło im o to, że importinstrukcje mają nadawać się do analizy statycznej - ponieważ nigdy nie są warunkowe, narzędzia mogą łatwiej analizować drzewa zależności.
Joe Clay
4
Trudne do zrozumienia przy użyciu „foo”, „baz” i „bar” - co powiesz na przykład z życia wzięty?
TetraDev
1
To już nie jest prawdą. Dynamiczny import to teraz rzecz. Zobacz tutaj: stackoverflow.com/a/46543949/687677
superluminary
7

Ponieważ to pytanie jest wysoko oceniane przez Google, warto zauważyć, że sytuacja uległa zmianie od czasu opublikowania starszych odpowiedzi.

MDN ma ten wpis w Importach dynamicznych :

Słowo kluczowe import można wywołać jako funkcję dynamicznego importu modułu. Używany w ten sposób zwraca obietnicę.

import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

// This form also supports the await keyword.
let module = await import('/modules/my-module.js');

Przydatny artykuł na ten temat można znaleźć na Medium .

LeeGee
źródło
1

Wymaganie nie rozwiąże problemu, ponieważ jest to połączenie synchroniczne. Istnieje kilka opcji i wszystkie obejmują

  1. Pytanie o potrzebny moduł
  2. Czekam na obietnicę zwrotu modułu

W skrypcie ECMA istnieje wsparcie dla leniwego ładowania modułów przy użyciu SystemJS. To oczywiście nie jest obsługiwane we wszystkich przeglądarkach, więc w międzyczasie możesz użyć JSPM lub podkładki SystemJS.

https://github.com/ModuleLoader/es6-module-loader

Henrik Vendelbo
źródło
1

Od 2016 roku wiele się wydarzyło w świecie JavaScript, więc wierzę, że czas przedstawić najbardziej aktualne informacje na ten temat. Obecnie dynamiczne importowanie jest rzeczywistością zarówno w Node, jak i w przeglądarkach (natywnie, jeśli nie obchodzi cię IE, lub z @ babel / plugin-syntax-dynamic-import, jeśli cię to obchodzi).

Rozważmy więc przykładowy moduł something.jsz dwoma nazwanymi eksportami i jednym domyślnym eksportem:

export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')

Możemy użyć import()składni, aby łatwo i czysto załadować ją warunkowo:

if (somethingIsTrue) {
  import('./something.js').then((module) => {
    // Use the module the way you want, as:
    module.hi('Erick') // Named export
    module.bye('Erick') // Named export
    module.default() // Default export
  })
}

Ale ponieważ zwrot jest a Promise, możliwy jest również cukier async/ awaitsyntaktyczny:

async imAsyncFunction () {
  if (somethingIsTrue) {
    const module = await import('./something.js')
    module.hi('Erick')
  }
}

A teraz pomyśl o możliwościach wraz z przypisaniem do niszczenia obiektów ! Na przykład, jesteśmy w stanie łatwo umieścić w pamięci tylko jeden z tych nazwanych eksportów do późniejszego wykorzystania:

const { bye } = await import('./something.js')
bye('Erick')

A może weź jeden z tych nazwanych eksportów i zmień jego nazwę na dowolną inną:

const { hi: hello } = await import('./something.js')
hello('Erick')

Lub nawet zmień nazwę domyślnej eksportowanej funkcji na coś, co ma większy sens:

const { default: helloWorld } = await import('./something.js')
helloWorld()

Ostatnia (ale nie mniej ważna) uwaga: import() może wyglądać jak wywołanie funkcji, ale nie jest Function. Jest to specjalna składnia, która po prostu używa nawiasów (podobnie jak w przypadku super()). Więc nie jest możliwe przypisanie importdo zmiennej lub użycie rzeczy z Functionprototypu, takich jak call/ apply.

Erick Petrucelli
źródło