模块与包的学习,对于入门nodejs已经足够,写这一部分是因为在学习nodejs过程中看到了一些个人认为比较好的资料,总结一下笔记,方便日后温习!通过本博客学习的初学者朋友可以直接跳过这个章节。
这一部分主要讲的就是一个内容,nodejs的模块加载机制。
Nodejs的模块分为两类,一类为原生模块,另外一类为文件模块。原生模块在nodejs远吗编译时候生成了二进制文件,加载速度最快。另外一类文件模块是动态加载的,加载速度比原生模块慢。
Nodejs对原生模块和文件模块都有缓存机制,这就是在NodeJS学习笔记(二) 模块与包_基础部分中require()的单次加载有介绍。通常模块只生成一次,每一次引用的都是同一块地址。缓存的地址:原生模块在nodejs的lib目录中,文件模块不确定。
Nodejs文件模块加载
Nodejs中文件模块问为三类:
1à.js文件模块。通过fs模块同步读取js文件并编译执行。
2à.node文件模块。通过C/C++编写的Addon。通过dlopen方法进行加载。
3à.json文件模块。通过矫勇JSON.parse解析加载。
用一个例子解释一下js文件的解析编译。
-
var test = require('./test.js');
-
console.log("helloWorld")
在nodejs编译这个代码的时候包代码包装成一个function(){}对象,里面传入了exports,require,module,_filename,_dirname这几个参数,这就是为什么在代码中没有引入require就可以直接使用require对象。
-
(function(exports,require,module,_filename,_dirname){
-
var test = require('./test.js');
-
console.log("helloWorld");
-
});
一般通过代码加载的模块都为文件模块,原生模块在启动时已经加载了。文件模块的加载工作主要由原生模块module完成的。Module.load(filename)。上面代码中,exports是module的构造函数初始化的一个空对象{}。通过require方法引入其他的模块。这个require方法调用的是load方法。这就是定义的文件模块,只有定义在exports对象上才能被外部调用的原因,在NodeJS学习笔记(二) 模块与包_基础部分中引用模块中的MyHello对象的另一种方式是
-
module.exports = MyHello;
-
var obj_MyHello = require('./exports_test.js');
就是把exports的对象直接指向MyHello。故而能够直接使用require直接拿到MyHello对象。
Require方法中的文件查找策略
从上图中可以看到,虽然原生模块和文件模块的优先级不同,但是如果文件模块和原生模块重名,肯定是先加载原生模块的。原生模块在初始node的时候已经加载入内存,如果重名,第一步肯定会没有在文件模块缓冲区中。故而原生模块拥有最高的家在优先级。
上面的示意图是加载的逻辑过程,在文件夹在是通常会有文件搜索查找过程,如下图:
在上一节中简单的说了一下require加载模块是的过程,上图是更为详细的查找过程。
à图中标注的虚线方框1为在 绝对路劲的查找过程,这个过程只进行1遍,如果没有,抛出异常。
à如果直接输入的是模块名称没有路径require('xxx')或者是使用的相对路径。会搜索虚线方框3中的过程,在每一级目录的node_modules目录查找是否有当前模块如果没有,进行虚线方框2后,重复虚线方框1的过程。
总结上述两种搜索过程,绝对路径的、相对路径的过程,绝对路径不会遍历每一个mode_modules,所以速度最快。
在node中有缓存机制,所以不用每一次加载模块都会有上述这么复杂的查找过程。