任何不把configuration绑定到Node.js中的全局对象的原因?

我正在寻找在我的Node应用程序中使用全局configuration设置的最佳方法。 按照(我)的偏好,我find的方法是:

  1. 将configuration附加到全局对象

    global.config = { db: require('./config/db'), server: require('./config/server'), session: require('./config/session'), auth: require('./config/auth') }; 
  2. 将configuration对象传递给需要它的模块。

     var config = { db: require('./config/db'), server: require('./config/server'), session: require('./config/session'), auth: require('./config/auth') }; var responder = require('./responder')(config); 
  3. 需要在每个模块中的configuration文件。 由于我通常将我的configuration分割成单独的文件,我真的不喜欢这样做。 由于我不总是使用某些文件,这通常也会检查文件是否存在。

有什么理由避免这两种方法? 有什么理由为什么比其他人更喜欢?

对于小项目来说,这三种方式都是可以接受 对于大的我可以说下一个:

全局variables是一个问题

如果你开始使用这种方式,你需要捍卫configuration对象,如var config = {...}; config.freeze(); var config = {...}; config.freeze(); 。 在任何情况下,对于NodeJS而言,全局variables都是不好的做法,因为它会破坏模块化系统。

传递configuration是最好的方法

这就是原因? testing

在testing中,你需要得到你的configuration文件的一些状态。 第一种和第三种方式为您提供了下一代码风格:

config.js

 module.exports= { a: 10 }; 

app.js

 var config = require('config'); module.exports.func = function(){ if (config.a > 10) return 'A'; return 'B'; } 

摩卡+柴testing

 var expect = require('chai').except, config = require('config'), app = require('app'); describe('Func', function(){ it('return "A" if a > 10', function(){ config.a = 12; //DHOOO!!! (c) Homer Simpson expect(app.func()).to.equal('A'); }); it('return "B" if a <= 10', function(){ config.a = 9; expect(app.func()).to.equal('B'); }); config.a = 12; //return default state of config. DHOOO!!! }); 

你怎么看你需要有可编辑的configuration,这是一个不好的做法(每个开发人员可以在任何地方改变configuration状态的大项目… DHOOO !!!)

第二种方式看起来像这样:

config.js

 var config = { a: 10 }; config.freezy(); module.exports = config; 

app.js

 module.exports.func = function(config){ if (config.a > 10) return 'A'; return 'B'; } 

摩卡+柴testing

 var expect = require('chai').except, app = require('app'); describe('Func', function(){ it('return "A" if a > 10', function(){ expect(app.func({a:12})).to.equal('A'); }); it('return "B" if a <= 10', function(){ expect(app.func({a:9})).to.equal('B'); }); }); 

UPDATE

在这个例子中, func是非常同步的,对于真正的项目你可以看到如下的东西:

module.js

 var SubModule = require('submodule'); function MyModule(config, someVar) { //Don't use full config, only options you needed. //Pull out config options this._a = config.a; this._b = config.b; this.doSomethink(someVar); this.subModule = new SubModule(config); } MyModule.prototype.doSomething = function(){ if (this._a > 10) return 'A'; return 'B'; } module.exports = MyModule;` 

submodule.js

 function MySubModule(config) { this._c = config.c; } module.exports = MySubModule; 

根据我的经验,使用第二种select是很常见的方式:将configuration选项传递给需要的模块

理由

  • 它将configuration从实际的逻辑中分离出来。 如果在模块本身中包含configuration文件,则对一个特定的configuration文件会产生不必要的依赖关系。
  • 对于特定的configuration值,仍然有一个定义的依赖关系,这些依赖关系是作为参数提供的 – 而不是从全局命名空间“神奇地”拉出来,这使得代码难以读取,维护和testing。

顺便说一下,几乎所有的语言都可以使用全局variables/对象和结构来包含“你喜欢的任何地方”。 但是requirejs已经把你推向了一个正确的方向,至less允许exports是一个立即接受configuration的函数。 所以单线是要求和configuration资源的一个很好的方法。

除此之外的所有内容都可能最终在关于dependency injection(DI)概念的讨论中 – 这是一个单独的主题。