Ist es möglich, Module mit einem Platzhalter aus allen Dateien in einem Verzeichnis zu importieren?

256

Mit ES6 kann ich mehrere Exporte aus einer Datei wie dieser importieren:

import {ThingA, ThingB, ThingC} from 'lib/things';

Ich mag jedoch die Organisation, ein Modul pro Datei zu haben. Am Ende habe ich folgende Importe:

import ThingA from 'lib/things/ThingA';
import ThingB from 'lib/things/ThingB';
import ThingC from 'lib/things/ThingC';

Ich würde gerne dazu in der Lage sein:

import {ThingA, ThingB, ThingC} from 'lib/things/*';

oder etwas Ähnliches, mit der verstandenen Konvention, dass jede Datei einen Standardexport enthält und jedes Modul den gleichen Namen wie seine Datei hat.

Ist das möglich?

Frambot
quelle
Das ist möglich. Weitere Informationen finden Sie in der Moduldokumentation zu babel babeljs.io/docs/learn-es2015 ... mit der Bezeichnung "import {sum, pi} from" lib / math ";". Die akzeptierte Antwort ist nicht mehr gültig. Bitte aktualisieren Sie es.
Eduard Jacko
6
@kresli Ich glaube nicht, dass du die Frage verstehst. In den Dokumenten lib/mathbefindet sich eine Datei, die mehrere Exporte enthält. In meiner Frage lib/math/ist ein Verzeichnis mit mehreren Dateien, die jeweils einen Export enthalten.
Frambot
2
OK, ich verstehe. In diesem Fall ist Bergi richtig. Entschuldigung
Eduard Jacko

Antworten:

231

Ich denke nicht, dass dies möglich ist, aber afaik die Auflösung von Modulnamen hängt von den Modulladern ab, so dass es möglicherweise eine Loader-Implementierung gibt, die dies unterstützt.

Bis dahin könnten Sie eine Zwischen- "Moduldatei" verwenden lib/things/index.js, die nur enthält

export * from 'ThingA';
export * from 'ThingB';
export * from 'ThingC';

und es würde dir erlauben zu tun

import {ThingA, ThingB, ThingC} from 'lib/things';
Bergi
quelle
6
Danke für die Hilfe. Ich konnte das zum Laufen bringen, index.jsindem ich aussah wie : import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};. Andere Beschwörungsformeln index.jswürden sich nicht rühren.
Frambot
2
Hm, export * fromsollte funktionieren. Hast du es versucht …from './ThingA'oder export ThingA from …? Welchen Modullader verwenden Sie?
Bergi
7
Ja, Ihre ursprüngliche Antwort hat funktioniert, wenn jedes ThingA.js, ThingB.js, jedes exportierte benannte Exporte. Genau richtig.
Frambot
1
Müssen Sie die Indexdatei angeben oder können Sie nur den Ordner angeben und stattdessen wird index.js geladen?
Zorgatone
1
@Zorgatone: Das hängt vom verwendeten Modullader ab, aber normalerweise reicht der Ordnerpfad aus.
Bergi
128

Nur eine Variation des Themas, das bereits in der Antwort enthalten ist, aber wie wäre es damit:

In a Thing,

export default function ThingA () {}

In things/index.js,

export {default as ThingA} from './ThingA'
export {default as ThingB} from './ThingB'
export {default as ThingC} from './ThingC'

Dann, um alle Dinge woanders zu konsumieren,

import * as things from './things'
things.ThingA()

Oder um nur einige Dinge zu konsumieren,

import {ThingA,ThingB} from './things'
Jed Richards
quelle
Willst du dir die Antwort von @ wolfbiter ansehen? Ich bin mir nicht sicher, warum er behauptet, dass Klammern nicht funktionieren.
Bergi
@Bergi Ja, stimme zu, ich glaube nicht, dass Wolfbiters gültiges ES6 ist. Vielleicht benutzt er eine alte Version von Babel oder einen anderen Transpiler?
Jed Richards
Wie wird das umgesetzt? Das Importieren eines Verzeichnisses löst sich index.jsfür mich nicht auf. Ich benutze SystemJs + Babel
Jasonszhao
2
Sie können nicht einfach geben export ThingA from './ThingA'stattexport {default as ThingA} from './ThingA'
Petr Peller
1
nutzt dies drei Schütteln? Wenn ich {ThingA} aus './things' importiere, werden dann auch ThingB und ThingC zum Bundle hinzugefügt?
Giorgio
75

Die aktuellen Antworten deuten auf eine Problemumgehung hin, aber es nervt mich, warum dies nicht existiert. Deshalb habe ich ein babelPlugin erstellt, das dies tut.

Installieren Sie es mit:

npm i --save-dev babel-plugin-wildcard

dann füge es deinem hinzu .babelrcmit:

{
    "plugins": ["wildcard"]
}

Ausführliche Informationen zur Installation finden Sie im Repo


Dies ermöglicht Ihnen Folgendes:

import * as Things from './lib/things';

// Do whatever you want with these :D
Things.ThingA;
Things.ThingB;
Things.ThingC;

Auch hier enthält das Repo weitere Informationen darüber, was genau es tut. Auf diese Weise wird jedoch das Erstellen von index.jsDateien vermieden, und dies geschieht auch zur Kompilierungszeit, um zu vermeiden, dass readdirs zur Laufzeit ausgeführt wird.

Auch mit einer neueren Version können Sie genau wie in Ihrem Beispiel vorgehen:

 import { ThingsA, ThingsB, ThingsC } from './lib/things/*';

funktioniert genauso wie oben.

Downgoat
quelle
3
Achtung, ich habe schwerwiegende Probleme mit diesem Plugin. Probleme entstehen wahrscheinlich durch das interne Caching. Sie werden sich die Haare ausreißen, wenn Ihr Code perfekt ist, aber Ihr Skript funktioniert nicht richtig, weil Sie eine Datei hinzugefügt haben ./lib/things;und diese nicht erfasst wird. Die Probleme, die es verursacht, sind lächerlich. Ich habe gerade eine Situation erlebt, in der das Ändern der Datei mit import *make babel die aufgenommene Datei aufnimmt, aber das Zurücksetzen bringt das Problem zurück, als würde der Cache vor der Änderung wiederverwendet. Mit Vorsicht verwenden.
Łukasz Zaroda
@ ŁukaszZaroda babel hat einen internen Cache, ~/.babel.jsonder dieses seltsame Verhalten verursacht. Auch wenn Sie wie ein Watcher oder ein Hot Reloader verwenden, müssen Sie die neue Datei speichern, damit sie mit der neuen Verzeichnisliste neu kompiliert wird
Downgoat
@Downgoat, wie kann man das überwinden, außer den Cache von Babel zu löschen? Und übrigens. Ich denke nicht, dass Ihr Kommentar richtig ist. Ich habe Babels Caching deaktiviert und hatte so große Probleme mit diesem Plugin.
Absolut
1
Übrigens für alle, die weitere Probleme haben, fügen bpwc clear-cacheSie hinzu, da Webpack und andere Build-Prozesse immer noch stillschweigend zwischengespeichert werden
Downgoat
Das ist eine großartige Idee, aber ich konnte sie auch nicht zum Laufen bringen. Möglicherweise ein Konflikt mit meinem Code vom Flusstyp, ich bin mir nicht sicher, aber ich habe "ReferenceError: Foo ist nicht definiert" erhalten, unabhängig davon, wie ich die Importe strukturiert habe.
Jlewkovich
13

Großartige Gugly Muglys! Das war schwieriger als es sein musste.

Exportieren Sie eine flache Standardeinstellung

Dies ist eine großartige Gelegenheit, Spread zu verwenden ( ...siehe { ...Matters, ...Contacts }unten:

// imports/collections/Matters.js
export default {           // default export
  hello: 'World',
  something: 'important',
};
// imports/collections/Contacts.js
export default {           // default export
  hello: 'Moon',
  email: '[email protected]',
};
// imports/collections/index.js
import Matters from './Matters';      // import default export as var 'Matters'
import Contacts from './Contacts';

export default {  // default export
  ...Matters,     // spread Matters, overwriting previous properties
  ...Contacts,    // spread Contacts, overwriting previosu properties
};
// imports/test.js
import collections from './collections';  // import default export as 'collections'

console.log(collections);

So führen Sie babel kompilierten Code über die Befehlszeile aus (vom Projekt root /):

$ npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node 
(trimmed)

$ npx babel-node --presets @babel/preset-env imports/test.js 
{ hello: 'Moon',
  something: 'important',
  email: '[email protected]' }

Exportieren Sie einen baumartigen Standard

Wenn Sie Eigenschaften lieber nicht überschreiben möchten, ändern Sie:

// imports/collections/index.js
import Matters from './Matters';     // import default as 'Matters'
import Contacts from './Contacts';

export default {   // export default
  Matters,
  Contacts,
};

Und die Ausgabe wird sein:

$ npx babel-node --presets @babel/preset-env imports/test.js
{ Matters: { hello: 'World', something: 'important' },
  Contacts: { hello: 'Moon', email: '[email protected]' } }

Exportieren Sie mehrere benannte Exporte ohne Standard

Wenn Sie sich DRY widmen, ändert sich auch die Syntax der Importe:

// imports/collections/index.js

// export default as named export 'Matters'
export { default as Matters } from './Matters';  
export { default as Contacts } from './Contacts'; 

Dadurch werden 2 benannte Exporte ohne Standardexport erstellt. Dann ändern Sie:

// imports/test.js
import { Matters, Contacts } from './collections';

console.log(Matters, Contacts);

Und die Ausgabe:

$ npx babel-node --presets @babel/preset-env imports/test.js
{ hello: 'World', something: 'important' } { hello: 'Moon', email: '[email protected]' }

Importieren Sie alle benannten Exporte

// imports/collections/index.js

// export default as named export 'Matters'
export { default as Matters } from './Matters';
export { default as Contacts } from './Contacts';
// imports/test.js

// Import all named exports as 'collections'
import * as collections from './collections';

console.log(collections);  // interesting output
console.log(collections.Matters, collections.Contacts);

Beachten Sie die Destrukturierung import { Matters, Contacts } from './collections'; im vorherigen Beispiel.

$ npx babel-node --presets @babel/preset-env imports/test.js
{ Matters: [Getter], Contacts: [Getter] }
{ hello: 'World', something: 'important' } { hello: 'Moon', email: '[email protected]' }

In der Praxis

Angesichts dieser Quelldateien:

/myLib/thingA.js
/myLib/thingB.js
/myLib/thingC.js

Das Erstellen eines /myLib/index.jszum Bündeln aller Dateien hat keinen Zweck zum Importieren / Exportieren. Es wäre einfacher, alles global zu machen, als alles global durch Import / Export über index.js "Wrapper-Dateien" zu machen.

Wenn Sie eine bestimmte Datei möchten, import thingA from './myLib/thingA';in Ihren eigenen Projekten.

Das Erstellen einer "Wrapper-Datei" mit Exporten für das Modul ist nur dann sinnvoll, wenn Sie für npm oder in einem mehrjährigen Multi-Team-Projekt packen.

Bis hierher geschafft? Weitere Informationen finden Sie in den Dokumenten .

Außerdem unterstützt Stackoverflow endlich drei als Code-Zaun-Markup.

Michael Cole
quelle
10

Sie können async import () verwenden:

import fs = require('fs');

und dann:

fs.readdir('./someDir', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );
  // or const module = await import('file')
  });
});
mr_squall
quelle
2
Dynamische Importe sind so schön. Sie existierten sicher nicht, als die Frage gestellt wurde. Danke für die Antwort.
Frambot
6

Ähnlich wie bei der akzeptierten Frage, aber Sie können skalieren, ohne jedes Mal, wenn Sie eines erstellen, ein neues Modul zur Indexdatei hinzufügen zu müssen:

./modules/moduleA.js

export const example = 'example';
export const anotherExample = 'anotherExample';

./modules/index.js

// require all modules on the path and with the pattern defined
const req = require.context('./', true, /.js$/);

const modules = req.keys().map(req);

// export all modules
module.exports = modules;

./example.js

import { example, anotherExample } from './modules'
Nicolas
quelle
Dies funktioniert nicht für mich, wenn ich versuche, als Alias ​​in./example.js
tsujp
funktioniert auch nicht für mich (Webpack 4.41, Babel 7.7)
Edwin Joassart
3

Ich habe sie einige Male verwendet (insbesondere zum Erstellen massiver Objekte, die die Daten auf viele Dateien aufteilen (z. B. AST-Knoten)). Um sie zu erstellen, habe ich ein winziges Skript erstellt (das ich gerade zu npm hinzugefügt habe, damit alle anderen kann es benutzen).

Verwendung (derzeit müssen Sie babel verwenden, um die Exportdatei zu verwenden):

$ npm install -g folder-module
$ folder-module my-cool-module/

Erzeugt eine Datei mit:

export {default as foo} from "./module/foo.js"
export {default as default} from "./module/default.js"
export {default as bar} from "./module/bar.js"
...etc

Dann können Sie einfach die Datei verbrauchen:

import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
Jamesernator
quelle
Funktioniert in Windows nicht richtig, generiert den Pfad als Windows-Pfad ( \` instead of / ) also as an improvment you may want to allow two options like --Dateiname` && --dest, um das Anpassen zu ermöglichen, wo die erstellte Datei gespeichert werden soll und unter welchem ​​Namen. Funktioniert auch nicht mit Dateinamen, die .(wie user.model.js) enthalten
Yuri Scarbaci
2

Nur eine andere Herangehensweise an @ Bergis Antwort

// lib/things/index.js
import ThingA from './ThingA';
import ThingB from './ThingB';
import ThingC from './ThingC';

export default {
 ThingA,
 ThingB,
 ThingC
}

Verwendet

import {ThingA, ThingB, ThingC} from './lib/things';
Ashok Vishwakarma
quelle
Es wird nicht funktionieren. Ich habe es gerade in einer Reaktions-App versucht und es kehrte zurück export '...' was not found in '.....
Hamid Mayeli
1

Sie können auch erfordern:

const moduleHolder = []

function loadModules(path) {
  let stat = fs.lstatSync(path)
  if (stat.isDirectory()) {
    // we have a directory: do a tree walk
    const files = fs.readdirSync(path)
    let f,
      l = files.length
    for (var i = 0; i < l; i++) {
      f = pathModule.join(path, files[i])
      loadModules(f)
    }
  } else {
    // we have a file: load it
    var controller = require(path)
    moduleHolder.push(controller)
  }
}

Verwenden Sie dann Ihren moduleHolder mit dynamisch geladenen Controllern:

  loadModules(DIR) 
  for (const controller of moduleHolder) {
    controller(app, db)
  }
mr_squall
quelle
0

Dies ist nicht genau das, wonach Sie gefragt haben, aber mit dieser Methode kann ich componentsListmeine anderen Dateien durchlaufen und Funktionen verwenden, wie componentsList.map(...)ich sie ziemlich nützlich finde!

import StepOne from './StepOne';
import StepTwo from './StepTwo';
import StepThree from './StepThree';
import StepFour from './StepFour';
import StepFive from './StepFive';
import StepSix from './StepSix';
import StepSeven from './StepSeven';
import StepEight from './StepEight';

const componentsList= () => [
  { component: StepOne(), key: 'step1' },
  { component: StepTwo(), key: 'step2' },
  { component: StepThree(), key: 'step3' },
  { component: StepFour(), key: 'step4' },
  { component: StepFive(), key: 'step5' },
  { component: StepSix(), key: 'step6' },
  { component: StepSeven(), key: 'step7' },
  { component: StepEight(), key: 'step8' }
];

export default componentsList;
FlyingZipper
quelle
0

Wenn Sie ein Webpack verwenden. Dadurch werden Dateien automatisch importiert und als API- Namespace exportiert .

Sie müssen also nicht bei jedem Hinzufügen von Dateien aktualisieren.

import camelCase from "lodash-es";
const requireModule = require.context("./", false, /\.js$/); // 
const api = {};

requireModule.keys().forEach(fileName => {
  if (fileName === "./index.js") return;
  const moduleName = camelCase(fileName.replace(/(\.\/|\.js)/g, ""));
  api[moduleName] = {
    ...requireModule(fileName).default
  };
});

export default api;

Für Typescript-Benutzer;

import { camelCase } from "lodash-es"
const requireModule = require.context("./folderName", false, /\.ts$/)

interface LooseObject {
  [key: string]: any
}

const api: LooseObject = {}

requireModule.keys().forEach(fileName => {
  if (fileName === "./index.ts") return
  const moduleName = camelCase(fileName.replace(/(\.\/|\.ts)/g, ""))
  api[moduleName] = {
    ...requireModule(fileName).default,
  }
})

export default api
atilkan
quelle
0

Ich konnte den Ansatz von Benutzer atilkan übernehmen und ein wenig ändern:

Für Typescript-Benutzer;

require.context('@/folder/with/modules', false, /\.ts$/).keys().forEach((fileName => {
    import('@/folder/with/modules' + fileName).then((mod) => {
            (window as any)[fileName] = mod[fileName];
            const module = new (window as any)[fileName]();

            // use module
});

}));
Justin Icenhour
quelle
-9

Wenn Sie nicht standardmäßig in A, B, C exportieren, sondern nur {} exportieren, ist dies möglich

// things/A.js
export function A() {}

// things/B.js
export function B() {}

// things/C.js
export function C() {}

// foo.js
import * as Foo from ./thing
Foo.A()
Foo.B()
Foo.C()
hjl
quelle
1
Dies ist kein gültiges Javascript (es gibt keine Anführungszeichen ./thing) und selbst wenn es vorhanden wäre, würde es nicht funktionieren. (Ich habe es versucht, und es hat nicht funktioniert.)
John