Nazwa importu zmiennej ES6 w node.js?


108

czy można zaimportować coś do modułu podając nazwę zmiennej podczas korzystania z importu ES6?

To znaczy chcę zaimportować jakiś moduł w czasie wykonywania w zależności od wartości podanych w konfiguracji:

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

1
@Bigood tak, kompilator wyskakuje i Webstorm również pokazuje błąd
— Vytautas Butkus

Odpowiedzi:


67

Nie z importoświadczeniem. importi exportsą zdefiniowane w taki sposób, że można je analizować statycznie, więc nie mogą zależeć od informacji o czasie wykonywania.

Szukasz API modułu ładującego (polyfill) , ale jestem trochę niejasny co do statusu specyfikacji:

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

3
czy muszę „wymagać” systemu? Nie ma zasięgu globalnego .. Ps Używam babel js
— Vytautas Butkus

Nie jest jeszcze nigdzie zaimplementowany AFAIK. Musisz użyć wypełnienia z linku.
— Felix Kling

1
Właśnie sprawdziłem, to trochę działa (próbuje załadować plik), ale potem psuje ścieżki dla innych modułów ... Szkoda, że ​​nie jest obsługiwany natywnie ..
— Vytautas Butkus

3
Czy to nadal problem? Muszę dynamicznie załadować moduły ES6, ale nie udało mi się.
— calbertts

26

Oprócz odpowiedzi Felixa , zauważę wyraźnie, że nie jest to obecnie dozwolone w gramatyce ECMAScript 6 :

Deklaracja importu :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • z ModuleSpecifier

ModułSpecifier :

  • StringLiteral

ModuleSpecifier może być tylko literał łańcuchowy , a nie innego rodzaju ekspresji niczym AdditiveExpression .


2
Szkoda, że ​​nie zostało to rozszerzone o const string literals. Można je analizować statycznie, prawda? Umożliwiłoby to ponowne wykorzystanie lokalizacji zależności. (np. zaimportuj szablon i miej dostęp do szablonu i lokalizacji szablonu).
— nicodemus13

26

Chociaż w rzeczywistości nie jest to import dynamiczny (np. W moich okolicznościach wszystkie pliki, które importuję poniżej, zostaną zaimportowane i dołączone do pakietu internetowego, a nie wybrane w czasie wykonywania), wzorzec, którego używam, który może pomóc w pewnych okolicznościach to :

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

const templates = {
  Template1,
  Template2
};

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

lub alternatywnie:

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

Nie sądzę, żebym mógł tak łatwo wrócić do wartości domyślnej za pomocą require(), co powoduje błąd, jeśli spróbuję zaimportować skonstruowaną ścieżkę szablonu, która nie istnieje.

Dobre przykłady i porównania między wymaganiami i importem można znaleźć tutaj: http://www.2ality.com/2014/09/es6-modules-final.html

Doskonała dokumentacja dotycząca ponownego eksportu z @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Interesuje mnie opinia na temat tego podejścia :)


Głosuj za. Użyłem podejścia „lub alternatywnie”. Zadziałało jak urok dla mojego niestandardowego rozwiązania lokalizacyjnego.
— groundh0g


1
Powinienem był o tym pomyśleć. Świetne rozwiązanie, bez względu na to, w jaki sposób importujesz rzeczy (i nawet jeśli niczego nie importujesz). Masz listę elementów, które chcesz później uzyskać nazwę lub nazwę? Umieść je w literale obiektu zamiast literału tablicowego i pozwól składni obiektu zająć się nazywaniem ich na podstawie lokalnej nazwy stałej / zmiennej. Jeśli potrzebujesz ich ponownie jako listy, po prostu zrób Object.values(templates).
— Andrew Koster

15

Istnieje nowa specyfikacja nazywana dynamicznym importem dla modułów ES. Po prostu dzwonisz import('./path/file.js')i jesteś gotowy. Funkcja zwraca obietnicę, która jest rozpatrywana z modułem, jeśli import się powiódł.

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

Przypadków użycia

Przypadki użycia obejmują importowanie komponentów oparte na trasach dla React, Vue itp. Oraz możliwość leniwego ładowania modułów , gdy są one wymagane w czasie wykonywania.

Dalsza informacja

Oto wyjaśnienie w Google Developers .

Zgodność z przeglądarkami (kwiecień 2020 r.)

Według MDN jest obsługiwany przez wszystkie obecnie popularne przeglądarki (z wyjątkiem IE), a caniuse.com wykazuje 87% poparcia w globalnym udziale w rynku. Znowu brak wsparcia w IE lub non-chromium Edge.


czy jesteś pewien swojej zmiany? propozycja pokazuje przykład ze zmienną ścieżką: github.com/tc39/proposal-dynamic-import#example
— phil294

@Blauhirn Byłem, ale Twój link wyraźnie pokazuje, że jest taka możliwość. Chociaż nie mam pojęcia, jak na przykład webpack rozwiązałby te importy
— Nicolai Schmid

popraw mnie, jeśli się mylę, ale webpack ich nie przetwarza, prawda? Pomyślałem, że chodzi o to, aby import dynamiczny działał „tak jak jest” w przeglądarce.
— phil294

Tak, możesz je uruchomić w przeglądarce bez zmian. Jednak pakiet webpack automatycznie wykorzystuje import, aby podzielić aplikację na wiele pakietów dla różnych części aplikacji, na przykład dla tras. Używam ich cały czas i są naprawdę pomocne. A jeśli chodzi o „przetwarzanie”; webpack przekaże import babel, który wypełni funkcję dla starszych przeglądarek.
— Nicolai Schmid

Dla jasności: dynamiczny import () będzie działał ze zmiennymi i nie musi być analizowalny statycznie (z pewnością o to właśnie chodzi w „dynamicznym”?). Zobacz moją odpowiedź.
— Velojet

6

Rozumiem pytanie zadane konkretnie dla ES6 importw Node.js, ale poniższe mogą pomóc innym szukającym bardziej ogólnego rozwiązania:

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

Uwaga, jeśli importujesz moduł ES6 i chcesz uzyskać dostęp do defaulteksportu, musisz użyć jednej z następujących opcji:

let variableName = "es6.js";

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

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

Możesz również użyć destrukturyzacji z tym podejściem, które może zwiększyć znajomość składni z innymi importami:

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

Niestety, jeśli chcesz uzyskać dostęp, defaulta także zniszczyć, musisz wykonać to w kilku krokach:

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

możesz do tego użyć notacji innej niż ES6. to działa dla mnie:

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

3

Mniej podoba mi się ta składnia, ale działa:
zamiast pisać

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

użyj tej składni:

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

1
@UlysseBN Różni się w zły sposób? Albo sposób, który tak naprawdę nie ma znaczenia?
— Sam

@Jacob, naprawdę są zupełnie inni, więc tak, to może mieć znaczenie w zależności od tego, co robisz. Pierwsza składnia jest oceniana statycznie, podczas gdy druga jest oceniana dynamicznie. Na przykład, jeśli używasz webpacka, nie będzie poprawnie wykonywać potrząsania drzewem z drugim. Istnieje wiele innych różnic, sugeruję przeczytanie tego dokumentu i sprawdzenie, który z nich jest bardziej odpowiedni dla Ciebie!
— Ulysse BN

@Jacob - nie ma znaczenia (w większości przypadków). require()to metoda Node.JS do ładowania plików, która jest wczesną wersją. importInstrukcja jest nowszą wersją, która jest teraz częścią składni języka urzędowego. Jednak w wielu przypadkach przeglądarka użyje poprzedniej (za nauką). Wymagane oświadczenie również spienięży twoje pliki, więc jeśli plik zostanie załadowany po raz drugi, zostanie załadowany z pamięci (lepsza wydajność). Sposób importu ma swoje zalety - jeśli używasz WebPack. wtedy webpack może usunąć martwe odnośniki (te skrypty nie zostaną pobrane do klienta).
— Gil Epshtain

1

Import dynamiczny () (dostępny w Chrome 63+) wykona Twoją pracę. Oto jak:

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

0

Zrobiłbym to w ten sposób

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...
}

zadzwoń z pliku

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
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.