Importname der ES6-Variablen in node.js?


108

Ist es möglich, während des ES6-Imports etwas in ein Modul mit Variablennamen zu importieren?

Dh ich möchte ein Modul zur Laufzeit importieren, abhängig von den in einer Konfiguration angegebenen Werten:

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

1
@Bigood ja, Compiler kotzt und Webstorm zeigt auch einen Fehler
Vytautas Butkus

Antworten:


67

Nicht mit der importAussage. importundexport sind so definiert, dass sie statisch analysierbar sind, sodass sie nicht von Laufzeitinformationen abhängen können.

Sie suchen nach der Loader-API (Polyfill) , aber ich bin mir über den Status der Spezifikation etwas unklar:

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

3
Muss ich ein System "benötigen"? Es ist nicht auf globaler Ebene .. Ps Ich benutze Babel Js
Vytautas Butkus

Es ist noch nirgendwo implementiert AFAIK. Sie müssen die Polyfüllung aus dem Link verwenden.
Felix Kling

1
Nur überprüft, es funktioniert irgendwie (versucht, die Datei zu laden), aber dann durcheinander Pfade für andere Module ... Schade, dass es nicht nativ unterstützt wird ..
Vytautas Butkus

3
Ist das noch ein Problem? Ich muss ES6-Module dinamisch laden, aber ich hatte keinen Erfolg.
Calbertts

26

Zusätzlich zu Felix 'Antwort werde ich ausdrücklich darauf hinweisen, dass dies derzeit in der ECMAScript 6-Grammatik nicht zulässig ist :

Importdeklaration :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • von ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

Ein ModuleSpecifier kann nur ein StringLiteral sein , keine andere Art von Ausdruck wie ein AdditiveExpression .


2
Schade, dass dies nicht um const string literals erweitert wurde. Sie sind statisch analysierbar, nicht wahr? Dies würde die Wiederverwendung des Ortes einer Abhängigkeit möglich machen. (z. B. eine Vorlage importieren und sowohl die Vorlage als auch den Speicherort der Vorlage verfügbar haben).
nicodemus13

26

Obwohl dies eigentlich kein dynamischer Import ist (z. B. werden unter meinen Umständen alle Dateien, die ich unten importiere, von einem Webpack importiert und gebündelt, das zur Laufzeit nicht ausgewählt wurde), ist ein von mir verwendetes Muster, das unter bestimmten Umständen hilfreich sein kann ::

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

const templates = {
  Template1,
  Template2
};

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

oder alternativ:

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

Ich glaube nicht, dass ich mit so einfach auf einen Standard zurückgreifen kann require(), was einen Fehler auslöst, wenn ich versuche, einen konstruierten Vorlagenpfad zu importieren, der nicht existiert.

Gute Beispiele und Vergleiche zwischen Anforderung und Import finden Sie hier: http://www.2ality.com/2014/09/es6-modules-final.html

Hervorragende Dokumentation zum erneuten Exportieren aus @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Ich bin interessiert, Feedback zu diesem Ansatz zu hören :)


Upvote. Ich habe den "oder alternativ" -Ansatz verwendet. Arbeitete wie ein Zauber für meine benutzerdefinierte Lokalisierungslösung.
Groundh0g


1
Ich hätte daran denken sollen. Tolle Lösung, egal wie Sie Dinge importieren (und selbst wenn Sie nichts importieren). Haben Sie eine Liste von Elementen, deren Namen oder Namen Sie später erhalten möchten? Fügen Sie sie in ein Objektliteral anstelle eines Array-Literals ein und lassen Sie die Objektsyntax sie anhand ihres lokalen Konstanten- / Variablennamens benennen. Wenn Sie sie erneut als Liste benötigen, tun Sie dies einfach Object.values(templates).
Andrew Koster

15

Es gibt eine neue Spezifikation, die als dynamischer Import für ES-Module bezeichnet wird. Grundsätzlich rufen Sie einfach an import('./path/file.js')und können loslegen. Die Funktion gibt ein Versprechen zurück, das mit dem Modul aufgelöst wird, wenn der Import erfolgreich war.

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

Anwendungsfälle

Anwendungsfälle umfassen routenbasierte Komponentenimport für React, Vue usw. und die Möglichkeit, Module zu verzögern , sobald sie zur Laufzeit benötigt werden.

Weitere Informationen

Hier ist eine Erklärung zu Google Developers .

Browserkompatibilität (April 2020)

Laut MDN wird es von jedem aktuellen großen Browser (außer IE) unterstützt und caniuse.com zeigt eine Unterstützung von 87% über den globalen Marktanteil. Wieder keine Unterstützung in IE oder Nicht-Chrom-Edge.


Bist du dir über deine Bearbeitung sicher? Der Vorschlag zeigt ein Beispiel mit einem variablen Pfad: github.com/tc39/proposal-dynamic-import#example
phil294

@Blauhirn war ich, aber dein Link zeigt deutlich, dass es eine Möglichkeit ist. Obwohl ich keine Ahnung habe, wie Webpack zum Beispiel diese Importe auflösen würde
Nicolai Schmid

Korrigieren Sie mich, wenn ich falsch liege, aber das Webpack verarbeitet diese nicht, oder? Ich dachte, es war der Punkt dynamischer Importe, dass sie "wie sie sind" im Browser ausgeführt werden.
Phil294

Ja, Sie können sie unverändert im Browser ausführen. Webpack verwendet die Importe jedoch automatisch, um Ihre App für verschiedene Teile Ihrer App, z. B. für Routen, in mehrere Bundles aufzuteilen. Ich benutze sie die ganze Zeit und sie sind wirklich hilfreich. Und was die "Verarbeitung" betrifft; Das Webpack übergibt die Importe an das Babel, wodurch die Funktion für ältere Browser mehrfach ausgefüllt wird.
Nicolai Schmid

Um klar zu sein: dynamisch import () wird mit Variablen arbeiten und ist nicht erforderlich statisch analysierbar sein (dh der ganze Sinn von ‚dynamisch‘ ist, oder ?). Siehe meine Antwort.
Velojet

6

Ich verstehe die Frage, die speziell für ES6 importin Node.js gestellt wurde, aber das Folgende könnte anderen helfen, nach einer allgemeineren Lösung zu suchen:

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

Hinweis: Wenn Sie ein ES6-Modul importieren und auf den defaultExport zugreifen müssen , müssen Sie eine der folgenden Optionen verwenden:

let variableName = "es6.js";

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

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

Sie können bei diesem Ansatz auch die Destrukturierung verwenden, um die Syntax mit Ihren anderen Importen besser vertraut zu machen:

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

Wenn Sie sowohl Zugriff defaultals auch Destrukturierung wünschen , müssen Sie dies leider in mehreren Schritten ausführen:

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

Sie können dazu die Nicht-ES6-Notation verwenden. Das hat bei mir funktioniert:

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

3

Ich mag diese Syntax weniger, aber sie funktioniert:
anstatt zu schreiben

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

Verwenden Sie diese Syntax:

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

1
@UlysseBN Auf schlechte Weise anders? Oder ein Weg, der eigentlich keine Rolle spielt?
Sam

@ Jacob, sie sind wirklich völlig anders, also könnte es wichtig sein, je nachdem, was du tust. Die erste Syntax wird statisch ausgewertet, während die zweite dynamisch ausgewertet wird. Wenn Sie beispielsweise ein Webpack verwenden, ist es nicht erforderlich, das Baumschütteln mit dem zweiten korrekt durchzuführen. Es gibt viele andere Unterschiede. Ich würde vorschlagen, dass Sie das Dokument lesen und herausfinden, welches für Sie besser geeignet ist!
Ulysse BN

@ Jacob - Egal (in den meisten Fällen). require()ist eine Node.JS-Methode zum Laden von Dateien. Dies ist die frühe Version. importAnweisung ist die neuere Version, die jetzt Teil der offiziellen Sprachsyntax ist. In vielen Fällen verwendet der Browser jedoch den vorherigen (hinter der Wissenschaft). Die require-Anweisung löst auch Ihre Dateien aus. Wenn eine Datei also zum zweiten Mal geladen wird, wird sie aus dem Speicher geladen (bessere Leistung). Der Import hat seine eigenen Vorteile - wenn Sie WebPack verwenden. Dann kann das Webpack tote Referenzen entfernen (diese Skripte werden nicht auf den Client heruntergeladen).
Gil Epshtain

1

Der dynamische Import () (verfügbar in Chrome 63+) erledigt Ihre Aufgabe. Hier ist wie:

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

0

Ich würde es so machen

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

Aufruf aus Datei

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.