任何不把configuration绑定到Node.js中的全局对象的原因?
我正在寻找在我的Node应用程序中使用全局configuration设置的最佳方法。 按照(我)的偏好,我find的方法是:
-
将configuration附加到全局对象
global.config = { db: require('./config/db'), server: require('./config/server'), session: require('./config/session'), auth: require('./config/auth') };
-
将configuration对象传递给需要它的模块。
var config = { db: require('./config/db'), server: require('./config/server'), session: require('./config/session'), auth: require('./config/auth') }; var responder = require('./responder')(config);
-
需要在每个模块中的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)概念的讨论中 – 这是一个单独的主题。