325

使用 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/*';

或类似的东西,每个文件都包含一个默认导出,并且每个模块的名称与其文件相同。

这可能吗?

4

14 回答 14

282

我认为这是不可能的,但是模块名称的解析取决于模块加载器,因此可能有一个加载器实现支持这一点。

在那之前,你可以使用一个中间的“模块文件” lib/things/index.js,它只包含

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

它会让你做

import {ThingA, ThingB, ThingC} from 'lib/things';
于 2015-04-18T20:45:15.223 回答
152

只是答案中已经提供的主题的变体,但是如何:

在一个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'
于 2015-07-14T09:16:01.883 回答
88

当前的答案提出了一种解决方法,但它让我感到困惑,为什么它不存在,所以我创建了一个babel插件来做到这一点。

安装它使用:

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

然后将其添加到您.babelrc的:

{
    "plugins": ["wildcard"]
}

有关详细的安装信息,请参阅repo


这使您可以这样做:

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

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

同样,repo包含有关它到底做什么的更多信息,但是这样做可以避免创建index.js文件,并且还会在编译时发生以避免readdir在运行时执行 s。

同样使用较新的版本,您可以完全按照您的示例进行操作:

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

工作方式与上述相同。

于 2017-04-20T00:27:33.450 回答
28

您现在可以使用异步导入():

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')
  });
});
于 2019-03-07T16:52:59.593 回答
26

伟大的大嘴猫!这比它需要的更难。

导出一个平面默认值

这是使用价差...的绝佳机会({ ...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 或多年多团队项目打包时,为模块创建带有导出的“包装文件”才有意义。

做到这一步了吗?有关更多详细信息,请参阅文档

此外,Stackoverflow 终于支持三个 `s 作为代码围栏标记了。

于 2019-02-18T21:46:18.313 回答
9

与已接受的问题类似,但它允许您进行扩展,而无需在每次创建索引文件时添加新模块:

./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'
于 2019-07-12T15:12:51.437 回答
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()
于 2017-03-13T23:20:43.750 回答
2

@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';
于 2018-05-11T11:58:44.880 回答
2

如果你使用的是 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;

对于打字稿用户;

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
于 2020-01-19T20:15:40.033 回答
2

节点?这样做:

使用 index.js 创建一个文件夹,在索引文件中添加:

var GET = require('./GET');
var IS = require('./IS');
var PARSE = require('./PARSE');
module.exports = { ...GET, ...IS, ...PARSE};

并且,在文件 GET.js 或 IS.js 中正常导出:

module.exports = { /* something as you like */}

现在,您只需要包含 index.js,例如:

const Helper = require('./YourFolder');

Helper将包含 YourFolder 中的所有功能。

再会!

于 2021-09-04T01:59:49.003 回答
1

您也可以使用 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)
  }
于 2019-03-19T10:01:03.623 回答
1

我能够从用户 atilkan 的方法中获取并对其进行一些修改:

对于打字稿用户;

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

}));
于 2020-05-07T05:30:40.003 回答
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;
于 2018-06-06T20:49:24.250 回答
-11

如果您不在 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()
于 2016-05-20T14:28:02.763 回答