Можно ли импортировать модули из всех файлов в каталоге, используя подстановочный знак?

С ES6 я могу импортировать несколько экспортированных файлов из такого файла:

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

Однако мне нравится организация наличия одного модуля на файл. Я получаю такой импорт:

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

Я хотел бы иметь возможность сделать это:

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

или что-то подобное, с понятным соглашением, что каждый файл содержит один экспорт по умолчанию, и каждый модуль называется так же, как его файл.

Это возможно?


person Joe Frambach    schedule 18.04.2015    source источник
comment
Это возможно. См. Документацию модуля для babel babeljs.io/docs/learn-es2015 ... guoted import {sum, pi} из lib / math ;. Принятый ответ больше не действителен. Пожалуйста, обновите его.   -  person Eduard Jacko    schedule 05.08.2015
comment
@kresli Я не думаю, что вы понимаете вопрос. В документации lib/math - это файл, содержащий несколько экспортированных файлов. В моем вопросе lib/math/ - это каталог, содержащий несколько файлов, каждый из которых содержит один экспорт.   -  person Joe Frambach    schedule 07.08.2015
comment
да я вижу. В этом случае Берги прав. Извините   -  person Eduard Jacko    schedule 08.08.2015


Ответы (13)


Я не думаю, что это возможно, но, как правило, разрешение имен модулей зависит от загрузчиков модулей, поэтому может быть реализация загрузчика, которая поддерживает это.

А пока вы можете использовать промежуточный "файл модуля" в lib/things/index.js, который просто содержит

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

и это позволит вам сделать

import {ThingA, ThingB, ThingC} from 'lib/things';
person Bergi    schedule 18.04.2015
comment
Спасибо за помощь. Мне удалось заставить это работать с index.js, выглядящим так: import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};. Другие заклинания в index.js не сдвинулись с места. - person Joe Frambach; 19.04.2015
comment
Хм, export * from должен работать . Вы пробовали …from './ThingA' или export ThingA from …? Какой загрузчик модулей вы используете? - person Bergi; 19.04.2015
comment
Или это потому, что вы используете экспорт по умолчанию вместо экспорта ThingA из ThingA? - person Bergi; 19.04.2015
comment
Я использую экспорт по умолчанию в каждом файле lib / things / ThingX.js. Похоже, это условность. Я использую webpack на iojs 1.7.1 - person Joe Frambach; 19.04.2015
comment
Да, ваш исходный ответ действительно работал, если каждый ThingA.js, ThingB.js экспортировал каждый именованный экспорт. Пятно на. - person Joe Frambach; 19.04.2015
comment
Кто-нибудь знает, существует ли уже существующая утилита для создания файла index.js? - person majorBummer; 28.09.2015
comment
@majorBummer - вы, вероятно, можете создать это с помощью GULP или как инструмент NPM. - person vsync; 26.10.2015
comment
Вам нужно указать индексный файл или вы можете указать только папку, и вместо этого будет загружен index.js? - person Zorgatone; 11.05.2016
comment
@Zorgatone: Это зависит от используемого загрузчика модуля, но да, обычно пути к папке будет достаточно. - person Bergi; 11.05.2016
comment
Я имею в виду. В стандартных модулях. Будет ли это работать в браузерах, когда они будут поддерживать модули, или они не смогут решить эту проблему? - person Zorgatone; 11.05.2016
comment
@Zorgatone: стандарта для модулей браузера пока нет. - person Bergi; 11.05.2016
comment
Разве модули ES6 не являются частью стандарта ES6? - person Zorgatone; 11.05.2016
comment
@Zorgatone: только синтаксис объявления, а не то, как они загружаются или как они идентифицируются по имени. - person Bergi; 11.05.2016
comment
если у вас есть export * from 'ThingA'; export * from 'ThingB';, как вы можете импортировать экспорт по умолчанию ThingA и ThingB из файла lib/things/index.js - person Olivier Boissé; 21.10.2019
comment
@ OlivierBoissé Если это экспорт по умолчанию, вам нужно явно дать им имя - экспорт звездочки не включает default, которые будут сталкиваться между модулями ThingA и ThingB. Если у вас есть и именованный экспорт, и экспорт по умолчанию, вам нужно использовать оба синтаксиса. - person Bergi; 21.10.2019
comment
Есть ли прямая выгода от использования этого подхода помимо удобочитаемости? - person Rodrigo Mata; 27.12.2019
comment
@RodrigoMata С каким альтернативным подходом вы сравниваете? - person Bergi; 28.12.2019
comment
@Bergi Я имею в виду, между наличием или отсутствием этого промежуточного файла модуля. ИМО это поможет, если, например, Я хочу переименовать какой-то конкретный export или, если мне нужно импортировать сразу много зависимостей, но я хотел бы знать, есть ли от этого дополнительные преимущества - person Rodrigo Mata; 28.12.2019
comment
@RodrigoMata Нет, на самом деле он просто обеспечивает эту группировку (и определенный порядок оценки, но это редко требуется). - person Bergi; 28.12.2019

Просто вариант темы, уже представленной в ответе, но как насчет этого:

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'

Затем потреблять все в другом месте,

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

Или потреблять только некоторые вещи,

import {ThingA,ThingB} from './things'
person Jed Richards    schedule 14.07.2015
comment
Хотите взглянуть на ответ @wolfbiter? Не уверен, почему он утверждает, что скобки не работают. - person Bergi; 05.08.2015
comment
@Bergi Да, согласен, я не думаю, что wolfbiter's - действительный ES6. Может он использует старую версию Babel или какой-нибудь другой транспиллер? - person Jed Richards; 05.08.2015
comment
Как это передается? Импорт каталога не решает index.js для меня. Я использую SystemJs + Babel - person user; 30.12.2015
comment
Разве вы не можете просто набрать export ThingA from './ThingA' вместо export {default as ThingA} from './ThingA' - person Petr Peller; 02.04.2016
comment
Я не знаю тонкостей спецификации синтаксиса модуля, но я просто попробовал это, и это синтаксическая ошибка. - person Jed Richards; 03.04.2016
comment
Вам не нужен default as ThingA - person lngs; 13.09.2016
comment
export ThingA from './ThingA' выдает синтаксические ошибки для валидатора VS Code JS - person Robert Hung; 20.02.2018
comment
Согласен, export ThingA from './ThingA', я думаю, недопустимый синтаксис. - person Jed Richards; 20.02.2018
comment
это использует трехкратное встряхивание? если я импортирую {ThingA} из './things', будут ли добавлены в пакет также ThingB и ThingC? - person Giorgio; 01.06.2019

Текущие ответы предлагают обходной путь, но меня беспокоило, почему этого не существует, поэтому я создал плагин babel, который делает это.

Установите его, используя:

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

затем добавьте его в свой .babelrc с помощью:

{
    "plugins": ["wildcard"]
}

см. репо для получения подробной информации об установке.


Это позволяет вам делать это:

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

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

опять же, репо содержит дополнительную информацию о том, что именно он делает, но это позволяет избежать создание index.js файлов, а также происходит во время компиляции, чтобы избежать выполнения readdirs во время выполнения.

Также с более новой версией вы можете сделать то же самое, что и ваш пример:

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

работает так же, как указано выше.

person Downgoat    schedule 20.04.2017
comment
Предупреждение, у меня серьезные проблемы с этим плагином. Проблемы, вероятно, происходят из-за его внутреннего кеширования, вы будете выдергивать волосы, когда ваш код будет идеальным, но ваш скрипт не будет работать должным образом, потому что вы добавили файл в ./lib/things;, а он не обрабатывается. Проблемы, которые он вызывает, просто смешны. Я только что был свидетелем ситуации, когда изменение файла с помощью import * заставляет babel подбирать добавленный файл, но изменение его обратно возвращает проблему, как будто он повторно использует кеш до изменения. Используйте с осторожностью. - person Łukasz Zaroda; 23.09.2017
comment
@ ŁukaszZaroda babel имеет внутренний кеш по адресу ~/.babel.json, что вызывает такое странное поведение. Также, если вы используете программу-наблюдатель или программу горячей перезагрузки, вам необходимо сохранить новый файл, чтобы он был перекомпилирован с новым списком каталогов. - person Downgoat; 23.09.2017
comment
@Downgoat, так как это преодолеть, кроме удаления кеша Babel? И кстати. Я не думаю, что ваш комментарий верен. У меня отключено кеширование babel, и у меня были огромные проблемы с этим плагином. Совершенно не рекомендую - person SOReader; 20.10.2017
comment
Кстати, если у вас возникнут дополнительные проблемы, добавьте bpwc clear-cache, потому что веб-пакет и другие процессы сборки по-прежнему будут молча кешировать - person Downgoat; 08.06.2018
comment
Это отличная идея, но мне тоже не удалось заставить ее работать. Возможно, это конфликт с моим потоковым кодом, я не уверен, но я получал «ReferenceError: Foo is not defined» независимо от того, как я структурировал импорт. - person jlewkovich; 15.06.2019
comment
@Downgoat Где мне добавить bpwc clear-cache? Спасибо - person Raine Revere; 14.12.2019
comment
У меня отлично работает. babel-загрузчик: ^ 8.0.6 - person Darin Cardin; 02.03.2020

Отличные гугли-мугли! Это было труднее, чем нужно.

Экспорт одного плоского по умолчанию

Это прекрасная возможность использовать spread ( ... в { ...Matters, ...Contacts } ниже:

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

Затем, чтобы запустить код, скомпилированный babel из командная строка (из корня проекта /):

$ 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]' }

Экспорт одного древовидного значения по умолчанию

Если вы не хотите перезаписывать свойства, измените:

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

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

И вывод будет:

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

Экспорт нескольких именованных экспортов без значения по умолчанию

Если вы посвятили себя DRY, синтаксис импорта также изменится:

// imports/collections/index.js

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

Это создает 2 именованных экспорта без экспорта по умолчанию. Затем измените:

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

console.log(Matters, Contacts);

И вывод:

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

Импортировать все именованные экспорты

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

Обратите внимание на деструктуризацию import { Matters, Contacts } from './collections'; в предыдущем пример.

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

На практике

Учитывая эти исходные файлы:

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

Создание /myLib/index.js для объединения всех файлов сводит на нет цель импорта / экспорта. Было бы проще сделать все глобальным в первую очередь, чем сделать все глобальным с помощью импорта / экспорта через «файлы-оболочки» index.js.

Если вам нужен конкретный файл, import thingA from './myLib/thingA'; в ваших собственных проектах.

Создание «файла-оболочки» с экспортом для модуля имеет смысл только в том случае, если вы упаковываете пакет для npm или в многолетнем проекте с несколькими командами.

Сделали это так далеко? Дополнительные сведения см. В документации.

Кроме того, ура для Stackoverflow, наконец, поддерживающего три символа в качестве разметки кода.

person Michael Cole    schedule 18.02.2019

Теперь вы можете использовать async import ():

import fs = require('fs');

а потом:

fs.readdir('./someDir', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );
  // or const module = await import('file')
  });
});
person mr_squall    schedule 07.03.2019
comment
Динамический импорт в этом плане хорош. Когда был задан вопрос, их точно не существовало. Спасибо за ответ. - person Joe Frambach; 07.03.2019
comment
Мне сложно понять, куда это пойдет. Можно ли его найти в файле index.js для загрузки всех файлов в каталоге? Можно вместо import('file') сделать еще export * from 'file'? Небольшая помощь? Спасибо! - person Joshua Pinter; 28.05.2021
comment
Это может быть какой-то метод начальной загрузки, где вы хотите, например, зарегистрировать все маршруты из файлов контроллера. - person mr_squall; 28.05.2021

Подобно принятому вопросу, но позволяет масштабировать без необходимости добавлять новый модуль в индексный файл каждый раз, когда вы его создаете:

./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'
person Nicolas    schedule 12.07.2019
comment
У меня не работает при попытке импорта в качестве псевдонима в ./example.js - person tsujp; 24.10.2019
comment
doens не работает и у меня (webpack 4.41, babel 7.7) - person Edwin Joassart; 30.12.2019

Я использовал их несколько раз (в частности, для создания массивных объектов, разделяющих данные на множество файлов (например, узлы AST)), чтобы создать их, я сделал крошечный скрипт (который я только что добавил в npm, чтобы все остальные можно использовать).

Использование (в настоящее время вам нужно использовать babel для использования файла экспорта):

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

Создает файл, содержащий:

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

Затем вы можете просто использовать файл:

import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
person Jamesernator    schedule 13.03.2017
comment
Не работает правильно в окнах, генерирует путь как путь Windows (_1 _ / _ 2 _-- filename` && --dest, чтобы разрешить настройку, где и под каким именем должен храниться созданный файл. Также не работает с именами файлов, содержащими . (например, user.model.js) - person Yuri Scarbaci; 16.01.2019

Просто другой подход к ответу @Bergi

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

export default {
 ThingA,
 ThingB,
 ThingC
}

Использует

import {ThingA, ThingB, ThingC} from './lib/things';
person Ashok Vishwakarma    schedule 11.05.2018
comment
Это не сработает. Я просто попробовал это в приложении реакции, и он вернул export '...' was not found in '..... - person Hamid Mayeli; 14.10.2019

Вы также можете использовать require:

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

Затем используйте свой moduleHolder с динамически загружаемыми контроллерами:

  loadModules(DIR) 
  for (const controller of moduleHolder) {
    controller(app, db)
  }
person mr_squall    schedule 19.03.2019

Если вы используете webpack. При этом файлы импортируются автоматически и экспортируются как пространство имен api.

Таким образом, нет необходимости обновлять каждый добавленный файл.

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;

Для пользователей Typescript;

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
person atilkan    schedule 19.01.2020

Это не совсем то, о чем вы просили, но с помощью этого метода я могу перебирать componentsList в других моих файлах и использовать такие функции, как componentsList.map(...), которые я считаю очень полезными!

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;
person FlyingZipper    schedule 06.06.2018

Мне удалось взять подход пользователя atilkan и немного его изменить:

Для пользователей Typescript;

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

}));
person Justin Icenhour    schedule 07.05.2020

если вы не экспортируете значения по умолчанию в A, B, C, а просто экспортируете {}, это возможно

// 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()
person hjl    schedule 20.05.2016
comment
Это недействительный javascript (вокруг ./thing нет кавычек), и даже если бы они были, это не сработало бы. (Я попробовал, и ничего не вышло.) - person John; 25.04.2017