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?


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

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'

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.


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: 'hello@example.com',
};
// 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: 'hello@example.com' }

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: 'hello@example.com' } }

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: 'hello@example.com' }

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: 'hello@example.com' }

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.


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')
  });
});

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'

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()

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

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

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;

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

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
});

}));

-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()

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