Có thể nhập các mô-đun từ tất cả các tệp trong một thư mục, sử dụng ký tự đại diện không?


256

Với ES6, tôi có thể nhập một số bản xuất từ ​​một tệp như thế này:

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

Tuy nhiên, tôi thích tổ chức có một mô-đun cho mỗi tệp. Tôi kết thúc với hàng nhập khẩu như thế này:

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

Tôi rất thích có thể làm điều này:

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

hoặc một cái gì đó tương tự, với quy ước được hiểu rằng mỗi tệp chứa một lần xuất mặc định và mỗi mô-đun được đặt tên giống như tệp của nó.

Điều này có thể không?


Điều này là khả thi. Vui lòng xem tài liệu mô-đun cho babel babeljs.io/docs/learn-es2015 ... được trích dẫn "nhập {sum, pi} từ" lib / math ";". Câu trả lời được chấp nhận là không còn giá trị. Hãy cập nhật nó.
Eduard Jacko

6
@kresli Tôi không nghĩ bạn hiểu câu hỏi. Trong các tài liệu, lib/mathlà một tệp chứa nhiều bản xuất. Trong câu hỏi của tôi, lib/math/là một thư mục chứa một số tệp, mỗi tệp chứa một xuất.
Frambot

2
ok, tôi hiểu rồi Trong trường hợp đó Bergi là chính xác. Xin lỗi
Eduard Jacko

Câu trả lời:


231

Tôi không nghĩ rằng điều này là có thể, nhưng vì độ phân giải của các tên mô-đun phụ thuộc vào các trình tải mô-đun nên có thể có một triển khai trình nạp hỗ trợ điều này.

Cho đến lúc đó, bạn có thể sử dụng một "tệp mô-đun" trung gian lib/things/index.jsmà chỉ chứa

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

và nó sẽ cho phép bạn làm

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

6
Cảm ơn đã giúp đỡ. Tôi đã có thể làm việc này với hình index.jsnhư : import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};. Câu thần chú khác index.jssẽ không nhúc nhích.
Frambot

2
Hừm, export * fromnên làm việc. Bạn đã thử …from './ThingA'hay export ThingA from …chưa? Bạn đang sử dụng bộ nạp mô-đun nào?
Bergi

7
Có, câu trả lời ban đầu của bạn đã hoạt động nếu mỗi ThingA.js, ThingB.js, mỗi xuất khẩu có tên xuất khẩu. Tại chỗ trên.
Frambot

1
Bạn có phải chỉ định tệp chỉ mục hoặc bạn có thể chỉ định thư mục và index.js sẽ được tải thay thế không?
Zorgatone

1
@Zorgatone: Điều đó phụ thuộc vào trình tải mô-đun bạn đang sử dụng, nhưng có thường thì đường dẫn thư mục sẽ là đủ.
Bergi

128

Chỉ là một biến thể về chủ đề đã được cung cấp trong câu trả lời, nhưng làm thế nào về điều này:

Trong một Thing,

export default function ThingA () {}

Trong things/index.js,

export {default as ThingA} from './ThingA'
export {default as ThingB} from './ThingB'
export {default as ThingC} from './ThingC'

Sau đó, để tiêu thụ tất cả những thứ khác,

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

Hoặc để tiêu thụ một số thứ,

import {ThingA,ThingB} from './things'

Bạn có muốn xem câu trả lời của @ wolfbiter không? Không chắc chắn tại sao anh ta tuyên bố dấu ngoặc đơn không hoạt động.
Bergi

@Bergi Vâng đồng ý, tôi không nghĩ người sói là ES6 hợp lệ. Có lẽ anh ta đang sử dụng một phiên bản cũ của Babel, hoặc một số bộ chuyển mã khác?
Jed Richards

Làm thế nào điều này được phiên mã? Nhập một thư mục không giải quyết index.jscho tôi. Tôi đang sử dụng SystemJs + Babel
jasonszhao

2
Bạn không thể đơn giản gõ export ThingA from './ThingA'thay vìexport {default as ThingA} from './ThingA'
Petr Peller

1
Điều này có lợi dụng ba lắc? nếu tôi nhập {ThingA} từ './things' thì ThingB và ThingC sẽ được thêm vào gói?
Giorgio

75

Các câu trả lời hiện tại cho thấy một cách giải quyết nhưng nó đã cho tôi biết tại sao điều này không tồn tại, vì vậy tôi đã tạo một babelplugin thực hiện điều này.

Cài đặt nó bằng cách sử dụng:

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

sau đó thêm nó vào của bạn .babelrcvới:

{
    "plugins": ["wildcard"]
}

xem repo để biết thông tin cài đặt chi tiết


Điều này cho phép bạn làm điều này:

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

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

một lần nữa, repo chứa thêm thông tin về chính xác những gì nó làm, nhưng thực hiện theo cách này để tránh tạo index.jstệp và cũng xảy ra vào thời gian biên dịch để tránh thực hiện readdirs khi chạy.

Ngoài ra với phiên bản mới hơn, bạn có thể thực hiện chính xác như ví dụ của mình:

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

hoạt động tương tự như trên.


3
Cảnh báo, tôi đang gặp vấn đề nghiêm trọng với plugin này. Các vấn đề có thể đến từ bộ nhớ đệm bên trong của nó, bạn sẽ nhổ tóc ra, khi mã của bạn hoàn hảo, nhưng tập lệnh của bạn sẽ không hoạt động chính xác vì bạn đã thêm tệp vào ./lib/things;và nó không được chọn. Những vấn đề nó gây ra là vô lý. Tôi vừa chứng kiến ​​tình huống, khi thay đổi tập tin với import *babel để chọn tập tin đã thêm, nhưng thay đổi nó trở lại, mang lại vấn đề, giống như nó sử dụng lại bộ đệm từ trước khi thay đổi. Sử dụng cẩn thận.
Łukasz Zaroda

@ ŁukaszZaroda babel có một bộ đệm trong ~/.babel.jsonđó gây ra hành vi kỳ lạ này. Ngoài ra, nếu bạn đang sử dụng như trình theo dõi hoặc trình tải lại nóng, bạn phải lưu tệp mới để nó sẽ được biên dịch lại với danh sách thư mục mới
Downgoat

@Downgoat vậy làm thế nào để khắc phục điều này ngoại trừ việc xóa bộ nhớ cache của babel? Và btw. Tôi không nghĩ nhận xét của bạn là đúng. Tôi đã vô hiệu hóa bộ nhớ đệm của babel và có một vấn đề rất lớn với plugin đó. Hoàn toàn không khuyến nghị điều đó
SOReader

1
Btw cho bất cứ ai gặp sự cố thêm, thêm bpwc clear-cachevì webpack và các quy trình xây dựng khác sẽ vẫn âm thầm lưu trữ bộ đệm
Downgoat

Đây là một ý tưởng tuyệt vời nhưng tôi cũng không thể làm cho nó hoạt động được. Có thể là một xung đột với mã lưu lượng của tôi, tôi không chắc chắn, nhưng tôi đã nhận được 'ReferenceError: Foo không được xác định' cho dù tôi có cấu trúc nhập khẩu như thế nào.
jlewkovich

13

Tuyệt vời Muglys! Điều này là khó khăn hơn nó cần phải được.

Xuất một mặc định phẳng

Đây là một cơ hội tuyệt vời để sử dụng lây lan ( ...{ ...Matters, ...Contacts }bên dưới:

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

Sau đó, để chạy mã biên dịch babel từ dòng lệnh (từ dự án root /):

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

Xuất một mặc định giống như cây

Nếu bạn không muốn ghi đè các thuộc tính, hãy thay đổi:

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

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

Và đầu ra sẽ là:

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

Xuất nhiều tên xuất khẩu không có mặc định

Nếu bạn dành riêng cho DRY , cú pháp nhập cũng thay đổi:

// imports/collections/index.js

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

Điều này tạo ra 2 xuất khẩu có tên không có xuất khẩu mặc định. Sau đó thay đổi:

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

console.log(Matters, Contacts);

Và đầu ra:

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

Nhập khẩu tất cả xuất khẩu có tên

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

Lưu ý sự phá hủy import { Matters, Contacts } from './collections'; trong ví dụ trước.

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

Trong thực tế

Cho các tệp nguồn này:

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

Tạo một /myLib/index.jsgói để tất cả các tệp đánh bại mục đích nhập / xuất. Việc tạo mọi thứ toàn cầu ở nơi đầu tiên sẽ dễ dàng hơn so với làm mọi thứ toàn cầu thông qua nhập / xuất thông qua "tệp bao bọc" của index.js.

Nếu bạn muốn một tập tin cụ thể, import thingA from './myLib/thingA';trong các dự án của riêng bạn.

Tạo một "tệp bao bọc" với xuất khẩu cho mô-đun chỉ có ý nghĩa nếu bạn đóng gói cho npm hoặc trong một dự án nhiều nhóm.

Làm cho nó đến nay? Xem các tài liệu để biết thêm chi tiết.

Ngoài ra, yay cho Stackoverflow cuối cùng cũng hỗ trợ ba `s dưới dạng đánh dấu hàng rào mã.


10

Bạn có thể sử dụng nhập không đồng bộ ():

import fs = require('fs');

và sau đó:

fs.readdir('./someDir', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );
  // or const module = await import('file')
  });
});

2
Nhập khẩu năng động là tốt đẹp như thế. Họ chắc chắn không tồn tại khi câu hỏi được hỏi. Cảm ơn câu trả lời.
Frambot

6

Tương tự như câu hỏi được chấp nhận nhưng nó cho phép bạn mở rộng quy mô mà không cần thêm mô-đun mới vào tệp chỉ mục mỗi khi bạn tạo một:

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

Điều này không hiệu quả với tôi khi cố gắng nhập dưới dạng bí danh trong./example.js
tsujp

không làm việc cho tôi cả (webpack 4.41, babel 7.7)
Edwin Joassart

3

Tôi đã sử dụng chúng một vài lần (đặc biệt là để xây dựng các đối tượng lớn chia dữ liệu trên nhiều tệp (ví dụ: các nút AST)), để tạo chúng, tôi đã tạo một tập lệnh nhỏ (mà tôi vừa thêm vào npm để mọi người khác co thể dùng nó).

Cách sử dụng (hiện tại bạn sẽ cần sử dụng babel để sử dụng tệp xuất):

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

Tạo một tệp chứa:

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

Sau đó, bạn chỉ có thể tiêu thụ các tập tin:

import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()

Không hoạt động chính xác trong windows, tạo đường dẫn dưới dạng đường dẫn windows ( \` instead of / ) also as an improvment you may want to allow two options like --filename` && --destđể cho phép tùy chỉnh nơi tệp được tạo sẽ được lưu trữ và dưới tên wich. Cũng không hoạt động với tên tệp có chứa .(như user.model.js)
Yuri Scarbaci

2

Chỉ là một cách tiếp cận khác để trả lời @ Bergi's

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

export default {
 ThingA,
 ThingB,
 ThingC
}

Công dụng

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

Nó sẽ không hoạt động. Tôi chỉ thử nó trong một ứng dụng phản ứng và nó trở lại export '...' was not found in '.....
Hamid Mayeli

1

Bạn cũng có thể sử dụng yêu cầu:

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

Sau đó sử dụng moduleHolder của bạn với các bộ điều khiển được tải động:

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

0

Đây không phải là chính xác những gì bạn yêu cầu, nhưng với phương pháp này, tôi có thể lặp lại componentsListtrong các tệp khác của mình và sử dụng chức năng như componentsList.map(...)tôi thấy khá hữu ích!

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

Nếu bạn đang sử dụng webpack. Điều này nhập tệp tự động và xuất dưới dạng không gian tên api .

Vì vậy, không cần phải cập nhật trên mỗi tập tin bổ sung.

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;

Đối với người dùng Bản in;

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

Tôi đã có thể lấy từ cách tiếp cận của người dùng atilkan và sửa đổi nó một chút:

Đối với người dùng Bản in;

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

nếu bạn không xuất mặc định trong A, B, C mà chỉ xuất {} thì có thể làm như vậy

// 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
Đây không phải là javascript hợp lệ (không có trích dẫn nào xung quanh ./thing) và thậm chí nếu có, nó sẽ không hoạt động. (Tôi đã thử nó và nó không hoạt động.)
John
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.