exports.hello = () => { console.log('hello'); }
浅谈Node模块系统及其模式(3)
如果是对exports重新赋值,也不会有影响,因为这个时候exports是一个新的对象,而不再是module.exports的引用,所以不会改变module.exports的内容。所以下面的代码是错误的
exports = () => { console.log('hello'); }
如果你想暴露的不是一个对象,或者是函数、实例或者是一个字符串,那可以通过module.exports来做
module.exports = () => { console.log('hello'); }
require函数是同步的
另外一个重要的我们需要注意的细节是,我们自建的require函数是同步的,事实上,它返回模块内容的方法很简单,并且不需要回调函数。Node内置的require()函数也是如此。因此,对于module.exports内容必须是同步的
// incorret code setTimeout(() => { module.exports = function(){} }, 100)
这个性质对于我们定义模块的方法十分重要,使得限制我们在定义模块的时候使用同步的代码。这也是为什么Node提供了很多同步API给我们的最重要的原因之一
如果我们需要定义一个异步操作来进行初始化的模块,我们也可以这么做,但是这种方法的问题是,我们不能保证require进来的模块能够准备好,后续我们会讨论这个问题的解决方案
其实,在早期的Node版本里,是有异步的require方法的,但是因为它的初始化时间和异步I/O所带来的性能消耗而废除了
resolving 算法
相依性地狱(dependency hell)描述的是由于软件之间的依赖性不能被满足从而导致的问题,软件的依赖反过来取决于其他的依赖,但是需要不同的兼容版本。Node很好的解决了这个问题通过加载不同版本的模块,具体取决于该模块从哪里被加载。这个特性的所有优点都能在npm上体现,并且也在require函数的resolving 算法中使用
然我们来快速连接下这个算法,我们都知道,resolve()函数获取模块名作为输入,然后返回一个模块的全路径,该路金用于加载它的代码也作为该模块唯一的标识。resolcing算法可以分为以下三个主要分支
- 文件模块(File modules),如果模块名是以"/"开始,则被认为是绝对路径开始,如果是以"./"开始,则表示为相对路径,它从使用该模块的位置开始计算加载模块的位置
- 核心模块(core modules),如果模块名不是"/"、"./"开始的话,该算法会首先去搜索Node的核心模块
- 包模块(package modules),如果通过模块名没有在核心模块中找到,那么就会继续在当前目录下的node_modules文件夹下寻找匹配的模块,如果没有,则一级一级往上照,直到到达文件系统的根目录