【问题标题】:Is it possible to import modules from all files in a directory, using a wildcard?是否可以使用通配符从目录中的所有文件导入模块?
【发布时间】:2019-04-05 13:04:06
【问题描述】:

使用 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} from "lib/math";"。接受的答案不再有效。请更新。
  • @kresli 我不认为你理解这个问题。在文档中,lib/math 是一个包含多个导出的文件。在我的问题中,lib/math/ 是一个包含多个文件的目录,每个文件都包含一个导出。
  • 好的,我明白了。在这种情况下,Bergi 是正确的。对不起

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

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

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

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

它会让你这样做

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

【讨论】:

  • 感谢您的帮助。我能够与index.js 一起工作,看起来像:import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};。 index.js 中的其他咒语不会让步。
  • 嗯,export * from 应该可以工作。你试过…from './ThingA'或export ThingA from …吗?你用的是什么模块加载器?
  • 是的,如果每个 ThingA.js、ThingB.js、每个都导出命名导出,您的原始答案确实有效。当场。
  • 您必须指定索引文件还是只指定文件夹并加载 index.js?
  • @RodrigoMata 不,它实际上只是提供了这种分组(并定义了评估顺序,但这很少需要)。
【解决方案2】:

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

在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 是的,我认为 wolfbiter 的 ES6 无效。也许他正在使用旧版本的 Babel 或其他转译器?
  • 这是如何编译的?对我来说,导入目录不会解析为 index.js。我正在使用 SystemJs + Babel
  • 你不能直接输入export ThingA from './ThingA' 而不是export {default as ThingA} from './ThingA'
  • 这是否利用了三摇?如果我从 './things' 导入 {ThingA} 也会将 ThingB 和 ThingC 添加到捆绑包中?
【解决方案3】:

当前的答案提出了一种解决方法,但我不明白为什么这不存在,所以我创建了一个 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 文件,并且还会在编译时发生,以避免在运行时执行 readdirs。

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

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

与上述相同。

【讨论】:

  • 警告,我在使用这个插件时遇到了严重的问题。问题可能来自其内部缓存,当您的代码完美时,您会感到头疼,但您的脚本将无法正常工作,因为您将文件添加到 ./lib/things; 并且它没有被拾取。它引起的问题是荒谬的。我刚刚目睹了这种情况,当使用import * 更改文件时,babel 会拾取添加的文件,但是将其更改回来,又会带来问题,就像它重用更改前的缓存一样。谨慎使用。
  • @ŁukaszZaroda babel 在~/.babel.json 有一个内部缓存,这会导致这种奇怪的行为。此外,如果您使用的是 watcher 或 hot reloader,您必须保存新文件,以便使用新目录列表重新编译它
  • @Downgoat 那么除了删除 babel 的缓存之外如何克服这个问题?顺便说一句。我不认为你的评论是正确的。我禁用了 babel 的缓存,并且该插件存在如此大的问题。完全不推荐
  • 顺便说一句,任何有其他问题的人,请添加bpwc clear-cache,因为 webpack 和其他构建过程仍然会静默缓存
  • 这是个好主意,但我也无法让它发挥作用。可能与我的流类型代码冲突,我不确定,但无论我如何构建导入,我都会收到“ReferenceError: Foo is not defined”。
【解决方案4】:

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

导出一个平面默认值

这是使用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: '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);

然后,到run babel compiled code from the command line(来自项目根目录/):

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

请注意前面示例中的destructuring 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 或多年多团队项目打包时,为模块创建带有导出的“包装文件”才有意义。

做到这一点了吗?有关详细信息,请参阅docs。

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • 动态导入就是这样。当问题被问到时,它们肯定不存在。感谢您的回答。
    • 我无法弄清楚这会去哪里。这会在index.js 文件中找到以加载目录中的所有文件吗?除了import('file'),你也可以做export * from 'file'吗?一点帮助?谢谢!
    • 这可以在一些引导方法中,例如,您希望从控制器文件中注册所有路由。
    • 这个答案对我有帮助。由于它包含 Node.js 代码,我将给出一些 Deno 代码作为补充。 const directory = new URL(".", import.meta.url).pathname;const fileItr = Deno.readDirSync(directory);const modulePromises = [...fileItr].map((fileInfo) => import(`${directory}${fileInfo.name}`));const modules = await Promise.all(modulePromises);
    【解决方案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 中导入别名时,这对我不起作用
    • 对我也不起作用(webpack 4.41,babel 7.7)
    【解决方案7】:

    我已经使用过几次(特别是用于构建大量对象,将数据拆分为多个文件(例如 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)
    【解决方案8】:

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

    【讨论】:

    • 这行不通。我刚刚在一个反应​​应用程序中尝试过,它返回了export '...' was not found in '....。
    【解决方案9】:

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

    【讨论】:

      【解决方案10】:

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

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

          Nodejs ?这样做:

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

          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 中的所有功能。

          美好的一天!

          【讨论】:

            【解决方案13】:

            这不完全是您所要求的,但是通过这种方法,我可以在我的其他文件中遍历 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;
            

            【讨论】:

              【解决方案14】:

              如果您不在 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()
              

              【讨论】:

              • 这不是有效的 javascript(./thing 周围没有引号),即使有,它也不起作用。 (我试过了,还是不行。)
              猜你喜欢
              • 2020-12-06
              • 1970-01-01
              • 1970-01-01
              • 2019-10-23
              • 2021-09-17
              • 2019-09-10
              相关资源
              最近更新 更多