现在明白了吧?其实我们在模块中像下面的代码那样,直接给exports赋值,会改变当前模块内部的形参exports对象的引用,也就是说当前的exports已经跟外部的module.exports对象没有任何关系了,所以这个改变是不会影响到module.exports的。因此,下面的这种方式是没有任何效果的,所有的属性和方法都不会被抛出。
//以下操作都是不起作用的
exports = \'Aphasia\';
exports = function(){
console.log(\'Aphasia\')
}
其实module.exports就是为了解决上述exports直接赋值,会导致抛出不成功的问题而产生的。有了它,我们就可以这样来抛出一个模块了。
//这些操作都是合法的
exports.name = \'Aphasia\';
exports.getName = function(){
console.log(\'Aphasia\')
}//相当于下面的方式module.exports = {
name: \'Aphasia\',
getName: function(){
console.log(\'Aphasia\')
}
}
这样就不用每次把要抛出的对象或方法赋值给exports的属性了 ,直接采用对象字面量的方式更加方便。
模块实例的require方法
我们都知道,当使用exports或者module.exports抛出一个模块,通过给require()方法传入模块标识符参数,然后node根据一定的规则引入该模块之后,我们就能使用模块中定义的方法和属性了。这里要讲的就是node的模块引入规则。
1、node中引入模块的机制
在Node中引入模块,需要经历3个步骤
(1)路径分析
(2)文件定位
(3)编译执行
在Node中,模块一般分为两种
(1)Node提供的模块,例如http、fs等,称为 核心模块 。核心模块在node源代码编译的过程中就编译进了二进制执行文件,在Node进程启动的时候,部分核心模块就直接加载进内存中了,因此这部分模块是不用经历上述的(2)(3)两个步骤的,而且在路径分析中是优先判断的,因此加载速度最快。
(2)用户自己编写的模块,称为 文件模块 。文件模块是按需加载的,需要经历上述的三个步骤,速度较慢。
优先从缓存中加载
与浏览器会缓存静态脚本文件以提高页面性能一样,Node对引入过的模块也会进行缓存。不同的地方是,node缓存的是编译执行之后的对象而不是静态文件。这一点我们可以用下面的方式来验证。
modA.js
console.log(\'模块modA开始加载...\')
exports = function() {
console.log(\'Hi\')
}console.log(\'模块modA加载完毕\')
init.js
var mod1 = require(\'./modA\')var mod2 = require(\'./modA\')console.log(mod1 === mod2)
执行 node init.js ,运行结果:
虽然我们两次引入modA这个模块,但是模块中的代码其实只执行了一遍。并且mod1和mod2指向了同一个模块对象。
下面是Module._load的源码:
Module._load = function(request, parent, isMain) {
// 计算绝对路径
var filename = Module._resolveFilename(request, parent);
// 第一步:如果有缓存,取出缓存
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
// 第二步:是否为内置模块
if (NativeModule.exists(filename)) {
return NativeModule.require(filename);
}
// 第三步:生成模块实例,存入缓存
var module = new Module(filename, parent);
Module._cache[filename] = module;
// 第四步:加载模块
try {
module.load(filename);
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}
// 第五步:输出模块的exports属性
return module.exports;
};
对应流程如下图所示:
2、路径分析和文件定位
路径分析
模块标识符分析:
(1)核心模块,如http、fs、path
(2)以 . 或 .. 开始的相对路径文件模块
(3)以 / 开始的绝对路径文件模块
(4)非路径形式的文件模块
1)核心模块:优先级仅次于缓存,加载速度最快;如果自定义模块与核心模块名称相同,加载是不会成功的。若想加载成功,必须选择一个不同的名称或者换用路径。