Nodejs:只使用index.js进行EXPORTS是一个好的实践吗?

24

我看到一些我继承的代码存在一个模式。每个目录都有自己的JS文件,但是还有一个名为index.js的文件,实际上从其他JS文件中导出项目。

我认为这样做是为了确切地知道你正在导出什么,因为主要的导出都在index.js中,而主要的代码在其他js文件中。

这个模式叫什么?这正确吗?

我应该继续使用这个模式吗?


不需要,index.js需要将包索引字段链接到索引文件。但是在模块内部,您可以使用相对路径加载任何文件。 - user4466350
3
这只是缩写,用于“require(要求)”整个目录。 - vp_arth
5个回答

25

假设我有以下目录结构:

MyApp
├── app.js
├── test.js
├── package.json
├─┬ controllers
│ ├── index.js
│ ├── signIn.js
│ └── signOut.js
└─┬ views
  ├── index.js
  ├── signIn.js
  └── signOut.js

将下面的代码放入index.js文件中...

// index.js
module.exports = {
  signIn: require('./signIn')
, signOut: require('./signOut')
};

...允许你像require一个完整的目录一样...

// test.js
describe('controllers', () => {
  // ~/controllers/index.js
  const controllers = require('./controllers');

  it('performs a sign-in', () => {
    ...
  });
  it('performs a sign-out', () => {
    ...
  });
});

另一种选择是逐个“require”每个文件。

在目录中拥有一个“index.js”并非必须。即使没有“index.js”,您也可以“require”目录中的文件。

// app.js
const signOut = require('./controllers/signOut.js')

然而,随着您的应用程序增长,这变得很繁琐。我使用类似require-directory这样的包,因为在目录中键入每个文件也很繁琐且有些容易出错。

// index.js
module.exports = require('require-directory')(module);

/*

This yields the same result as:

module.exports = {
  signIn: require('./signIn')
, signOut: require('./signOut')
, ...
};

*/

15

ES6 CommonJS模块语法

考虑到这两种常见的结构...

MyApp
│ // files divided per type (controllers, components, actions, ...)
├─┬ actions
│ ├── index.js
│ ├── signIn.js
│ └── signOut.js
├─┬ components ...
├─┬ reducers ...
├─┬ pages ...
│ 
│ // files divided per component
├─┬ components ...
│ ├── index.js 
│ ├── SimpleComponent.jsx
│ ├── AnotherComponent.duck.jsx // redux "duck" pattern
│ ├─┬ ComplexComponent // large complex logic, own actions, stylesheet, etc.
│ ...
├─┬ pages ...
│ ├── index.js 
│ ├─┬ App
│ │ ├── index.js // not necessary here, matter of habit
│ │ ├── App.jsx
│ │ ├── actions.js
│ │ └── reducer.js
│ └─┬ Dashboard
├── another.js
...

您可以像这样简单地导入another.js中的文件

import {signIn, signOut} from './actions'
import {App} from './pages'
import {ComplexComponent} from './components'

不使用index.js文件,应该这样做:(without index.js files)

import {signIn} from './actions/signIn'
import {signOut} from './actions/signOut'
import {App} from './pages/App/App' //notice the redundancy here
import {ComplexComponent} from './components/ComplexComponent/ComplexComponent'

更多阅读

ECMAScript 6 模块
import - JavaScript | MDN
Babel 转译器 - 现在可以将新的 import 引入您的浏览器中了

组织 React 项目
React Redux "Ducks pattern" - 组件的单文件解决方案


10

其他答案提供了很多很棒的信息,但是特别回答你的问题“我是否应该继续使用这种模式”,我会说不,至少大部分情况下是这样。

问题在于,这种模式需要额外的工作,因为你必须维护那些额外的index.js文件。在我的经验中,这种努力比只需编写一个目录更长的import语句所需的努力更大。此外,通过使用诸如require-dir之类的模块,您可以获得与具有index.js相同的功能而无需使用它。

尽管如此,如果你正在制作将由大量人使用的库,例如大型编程部门中的关键模块或公共NPM模块,则index.js的努力变得更加合理。只要有足够的人使用你的模块,你添加它们比你维护它们会节省更多的时间。


3

我将直接回答您是否应该使用这种模式的问题(因为其他答案对此不够充分)。

假设你代码中的每个目录都表示一个独立的模块(不依赖于其他模块工作)。使用这种模式将带来以下优点:

  1. 更好、更有组织的导入
  2. 每个模块内部/外部定义的分离(与在接口/API上使用private/public类似)

以下是这种模式的问题:

  1. 保持不同模块之间的松耦合可能非常繁琐(JS/TS不是纯OOP)
  2. 需要主动重构模块定义——存在更多循环依赖。
  3. 加载更多代码到内存中(即使未使用)——尽管我不确定这有多严重,因为在捆绑生产代码时通常会有优化来解决这个问题。

循环依赖非常棘手,使用index.js导入整个模块/目录将导入其中声明在index.js中的所有部分。因此,如果您有以下内容:

-- moduleA 
├-- comp1A // needs comp1B
├-- comp2A     
└-- index.js // export both comp1/2

-- moduleB
├-- comp1B 
├-- comp2B // needs comp2A
└-- index.js // export both comp1/2

示例案例 - comp1A 需要来自 comp1B 的内容,而 comp2B 需要来自 comp2A 的内容。

当导入特定文件(不带有 index.js - import something from './moduleB/comp1B')时,您将不会出现循环依赖。

但是,如果您使用 index.jsimport something from './moduleB'),则会发生循环依赖。

我建议在正确的地方使用 index.js 并保持维护!对于小模块使用 index.js 将非常完美,但随着时间的推移,它们将会增长并需要被拆分开来。在整个项目中使用公共 / 共享 / 工具 / 杂项 / 核心(无论您称其为何种未分类和不相关的代码)模块时,使用 index.js 是非常糟糕的。


1
这个是什么意思?
module.exports = {
  ...require('./moduleA'),
  ...require('./moduleB')
}

(moduleA.a将被moduleB.a覆盖)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接