Чи можливо імпортувати модулі з усіх файлів у каталозі, використовуючи підстановку?


256

За допомогою 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/*';

або щось подібне, з зрозумілою умовою, що кожен файл містить один експорт за замовчуванням, і кожен модуль називається таким же, як його файл.

Чи можливо це?


Це можливо. Перегляньте модульну документацію для babel babeljs.io/docs/learn-es2015 ... guoted "import {sum, pi} з" lib / math ";". Прийнята відповідь більше не є дійсною. Будь ласка, оновіть його.
Едуард Яцко

6
@kresli Я не думаю, що ви розумієте питання. У документах lib/mathє файл, що містить багаторазовий експорт. У моєму питанні lib/math/це каталог, що містить кілька файлів, кожен з яких містить один експорт.
Фрамбот

2
Добре, я бачу. У цьому випадку Бергі вірно. Вибачте
Едуард Ячко

Відповіді:


231

Я не думаю, що це можливо, але afaik дозвіл назв модулів залежить від завантажувачів модулів, тому може бути реалізація завантажувача, яка підтримує це.

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

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

і це дозволило б вам зробити

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

6
Дякую за допомогу. Я був в змозі отримати цю роботу з index.jsсхожим: import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};. Інші заклики index.jsне зміщуються.
Фрамбот

2
Гм, export * fromмає працювати. Ви пробували …from './ThingA'чи export ThingA from …? Який модуль-навантажувач ви використовуєте?
Бергі

7
Так, ваша оригінальна відповідь спрацювала, якщо кожен ThingA.js, ThingB.js, кожен експортований з ім'ям експорту. Пляма на.
Фрамбот

1
Чи потрібно вказати файл індексу або ви можете вказати лише папку, а замість нього буде завантажено index.js?
Zorgatone

1
@ Zorgatone: Це залежить від завантажувача модулів, який ви використовуєте, але так, зазвичай, шлях до папки буде достатній.
Бергі

128

Просто варіація теми, вже вказаної у відповіді, але як щодо цього:

В a Thing,

export default function ThingA () {}

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

Хочете поглянути на відповідь @ wolfbiter? Не впевнений, чому він стверджує, що дужки не працюють.
Бергі

@Bergi Так, погодьтеся, я не думаю, що вовчиця є дійсною ES6. Можливо, він використовує стару версію Babel чи якийсь інший транспілер?
Джед Річардс

Як це перекладається? Імпорт каталогу index.jsдля мене не вирішує . Я використовую SystemJs + Babel
jasonszhao

2
Хіба ви не можете просто ввести export ThingA from './ThingA'замістьexport {default as ThingA} from './ThingA'
Петро Пеллер

1
цим скористатися три струшування? якщо я імпортую {ThingA} з './things', також ThingB і ThingC будуть додані в комплект?
Джорджіо

75

Поточні відповіді дозволяють вирішити вирішення проблеми, але мені непомітно, чому цього не існує, тому я створив 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/*';

працює так само, як вище.


3
Увага: у мене виникли серйозні проблеми з цим плагіном. Проблеми, ймовірно, виникають із внутрішнього кешування, ви будете витягувати волосся, коли ваш код буде ідеальним, але ваш сценарій не буде працювати належним чином, оскільки ви додали файл ./lib/things;і він не підбирається. Проблеми, які він викликає, смішні. Я щойно був свідком ситуації, коли зміна файлу з допомогою import *примушує забрати доданий файл, але змінивши його назад, повертає проблему назад, наче вона повторно використовує кеш до зміни. Використовуйте обережно.
Łukasz Zaroda

@ ŁukaszZaroda babel має внутрішній кеш, в ~/.babel.jsonякому викликає цю дивну поведінку. Крім того, якщо ви використовуєте, як спостерігач або гарячий перевантажувач, вам потрібно зберегти новий файл, щоб він був перекомпільований з новим списком каталогів
Downgoat

@Downgoat так, як подолати це за винятком видалення кеш-пам’яті? І btw. Я не думаю, що ваш коментар є правильним. У мене вимкнено кешбек Babel і у мене були такі величезні проблеми з цим плагіном. Зовсім не рекомендую
SOReader

1
До уваги тих, хто має додаткові проблеми, додайте, bpwc clear-cacheтому що веб-пакунки та інші процеси збирання все ще мовчки кешуватимуть
Downgoat

Це відмінна ідея, але я не зміг змусити її працювати. Можливо, це конфлікт з моїм потоковим кодом, я не впевнений, але я отримував "ReferenceError: Foo не визначено", незалежно від того, як я структурував імпорт.
jlewkovich

13

Прекрасні гуглики! Це було складніше, ніж треба було.

Експорт одного плоского за замовчуванням

Це відмінна можливість використовувати поширення ( ...в { ...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: '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);

Потім, щоб запустити компільований код 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: 'hello@example.com' }

Експортуйте один за замовчуванням дерево

Якщо ви не хочете перезаписати властивості, змініть:

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

Експорт декількох названих експортів без замовчування

Якщо ви присвячені 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: 'hello@example.com' }

Імпортуйте весь названий експорт

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

На практиці

Враховуючи ці вихідні файли:

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

Створення /myLib/index.jsпакету для збереження всіх файлів розбиває мету імпорту / експорту. Було б простіше зробити все глобальне в першу чергу, ніж зробити все глобальним за допомогою імпорту / експорту через index.js "обгорткові файли".

Якщо ви хочете певного файлу, import thingA from './myLib/thingA';у власних проектах.

Створення "файлу обгортки" з експортом для модуля має сенс лише в тому випадку, якщо ви пакуєте для npm або для багаторічного багатокомандного проекту.

Зробили це далеко? Докладніше див. У документах .

Крім того, yay для Stackoverflow нарешті підтримує три `s як розмітку кодового паркану.


10

Ви можете використовувати імпорт async ():

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

2
Динамічний імпорт приємний. Вони впевнені, що їх не було, коли було задано питання. Дякую за відповідь.
Фрамбот

6

Подібно до прийнятого питання, але дозволяє масштабувати без необхідності додавання нового модуля в індексний файл щоразу, коли ви створюєте його:

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

Це не працює для мене при спробі імпортувати як псевдонім в./example.js
tsujp

ні для мене не працює (webpack 4.41, babel 7.7)
Edwin Joassart

3

Я декілька разів використовував їх (зокрема для побудови масивних об'єктів, що розділяють дані на багато файлів (наприклад, 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()

Неправильно працює у Windows, генерує шлях як шлях до Windows ( \` instead of / ) also as an improvment you may want to allow two options like --filename` &&, --destщоб дозволити налаштування, де створений файл повинен зберігатися та під яким ім'ям. Також не працює з назви файлів, що містять .(як user.model.js)
Юрій Скарбці

2

Просто інший підхід до відповіді @ Бергі

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

Це не спрацює. Я просто спробував це у реагуючому додатку, і він повернувся export '...' was not found in '.....
Хамід Майелі

1

Ви можете використовувати затребувати також:

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

Потім використовуйте модульHolder з динамічно завантаженими контролерами:

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

0

Це не саме те, про що ви просили, але, використовуючи цей метод, я можу відмітити сили 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;

0

Якщо ви використовуєте веб-пакет. Це імпортує файли автоматично та експортує як простір імен 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

0

Мені вдалося скористатися підходом користувача 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
});

}));

-9

якщо ви не експортуєте за замовчуванням в 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()

1
Це неправдивий JavaScript (немає цитат навколо ./thing), і навіть якби вони існували, він би не працював. (Я спробував це, і це не вийшло.)
Іван
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.