在Node应用中避免“Dot Hell”
在Node应用中,我们使用require
来加载模块。在目录层次相对复杂的应用中,总是会出现类似require('../../../../../module')
的调用,我把它称之为Dot Hell。我用了一些时间研究现有的解决方案,并介绍我个人认为最好的方法。

在Node中的全局对象是global
,它就像浏览器的window
对象一样。global
对象下面的方法都可以直接调用。
1 | global.a = 1 |
因此最简单的方法,也是我认为最好的方式就是在global
下创建一个appRequire
方法作为require
方法的包装,appRequire
方法专门用于调用应用内的包。
1 | var path = require('path') |
假设我们的项目目录结构如下
1 | ├── app |
其中app.js是应用的入口。那么我们只需要在app.js中应用上面的代码,那么在整个应用程序中就都可以使用了。
例如,现在在app/controller/AppController.js中,我们可以用下面的语句调用app/model/User.js。
1 | var User = appRequire('app/model/User') |
Oh Yeah! 一切都很优雅,很顺利。
但是一个应用中一定还会有测试代码。以单元测试为例,我们如果用mocha之类的Task Runner去运行测试的话,就得在每个测试前面都加上这一段代码,这样做很容易出错,而且很麻烦。
所以,我们可以把上述的封装代码单独封装成一个文件global-bootstrap.js,在运行mocha的时候,用mocha的require参数来指定每次运行测试之前要加载global-bootstrap.js。
1 | # 用Mocha运行tests文件夹下面的所有测试 |
其他方案
对于解决这个问题,还有两种方案:NODE_ENV方案(及其变种)和Symlink方案,你可以在这里看到。
我认为应该避免使用这两种方案。虽然这两种方案都可行,但是它们都会可能导致应用自身的目录名和node模块名冲突。例如,在下面的结构中,使用require('request')
就很容易产生二义性。
1 | . |
总结
我一直认为Node的模块引用方式的设计是有问题的,Dot Hell就很能说明这点。而Python相对而言就优雅很多,你可以直接通过路径的形式来导入包(在正确配置的情况下)。本文的解决方案允许我们用类似Python的方式去加载模块,你可以在我的项目webcraft中看到其应用。
让有趣易懂的知识主动找到你
订阅我的Email半月刊,让我们共同学习、成长。绝无广告!