是否有可能从目录中的所有文件导入模块,使用通配符?

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

或者类似的东西,按照大家理解的约定,每个文件包含一个默认导出,每个模块与其文件同名。

这可能吗?

341223 次浏览

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

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

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

它可以让你这么做

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

只是对答案中已经提供的主题的一个变化,但是这样如何:

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'

如果你不导出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()

我用过它们几次(特别是在构建大型对象时,将数据拆分到多个文件中(例如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()

目前的答案提出了一个解决方案,但它困扰我为什么这不存在,所以我创建了一个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文件,并且也发生在编译时,以避免在运行时执行__abc1。

同样,在一个更新的版本中,你可以完全像你的例子一样:

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

工作原理与上述相同。

这只是@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';

这并不完全是你所要求的,但是,有了这个方法,我可以在我的其他文件中迭代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;

太棒了!这比需要的要难。

导出一个平面默认值

这是使用传播(下面{ ...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' } }

导出多个命名的导出w/ no默认

如果你专注于,导入的语法也会改变:

// 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终于支持三个作为代码围栏标记。

你现在可以使用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')
});
});

你也可以使用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)
}

类似于公认的答案,但它允许你在每次创建索引文件时不需要添加一个新模块的情况下进行扩展:

/模块/ moduleA.js

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

/模块/ 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'

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

我可以借鉴用户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
});


}));

Nodejs吗?这样做:

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

助手将包含YourFolder中的所有功能。

美好的一天!