如果require参数一"/"开头,那么就以绝对路径的方式查找模块名称,如果参数一"./"、"../"开头,那么则是以相对路径的方式来
查找模块。
通过查找node_modules目录加载模块
如果require参数不以"/"、"./"、"../"开头,而该模块又不是核心模块,那么就要通过查找node_modules加载模块了。我们使用
的npm获取的包通常就是以这种方式加载的。
加载缓存
Node.js模块不会被重复加载,这是因为Node.js通过文件名缓存所有加载过的文件模块,所以以后再访问到时就不会重新加载
了。
注意:Node.js是根据实际文件名缓存的,而不是require()提供的参数缓存的,也就是说即使你分别通过require('express')和
require('./node_modules/express')加载两次,也不会重复加载,因为尽管两次参数不同,解析到的文件却是同一个。
Node.js 中的模块在加载之后是以单例化运行,并且遵循值传递原则:如果是一个对象,就相当于这个对象的引用。
模块载入过程
加载文件模块的工作,主要由原生模块module来实现和完成,该原生模块在启动时已经被加载,进程直接调用到runMain静态
方法。
具体说一下上文提到了文件模块的三类模块,这三类文件模块以后缀来区分,Node.js会根据后缀名来决定加载方法,具体的加
载方法在下文 require.extensions中会介绍。
.js 通过fs模块同步读取js文件并编译执行。
.node 通过C/C++进行编写的Addon。通过dlopen方法进行加载。
.json 读取文件,调用JSON.parse解析加载。
接下来详细描述js后缀的编译过程。Node.js在编译js文件的过程中实际完成的步骤有对js文件内容进行头尾包装。以app.js为
例,包装之后的app.js将会变成以下形式:
这就是为什么require并没有定义在app.js 文件中,但是这个方法却存在的原因。从Node.js的API文档中可以看到还有
__filename、 __dirname、 module、 exports几个没有定义但是却存在的变量。其中 __filename和 __dirname在查找文件路
径的过程中分析得到后传入的。 module变量是这个模块对象自身, exports是在module的构造函数中初始化的一个空对象
({},而不是null)。
在这个主文件中,可以通过require方法去引入其余的模块。而其实这个require方法实际调用的就是module._load方法。
load方法在载入、编译、缓存了module后,返回module的exports对象。这就是circle.js文件中只有定义在exports对象上的方
法才能被外部调用的原因。
以上所描述的模块载入机制均定义在lib/module.js中。
require 函数
require 引入的对象主要是函数。当 Node 调用 require() 函数,并且传递一个文件路径给它的时候,Node 会经历如下几个步
骤:
Resolving:找到文件的绝对路径;
Loading:判断文件内容类型;
Wrapping:打包,给这个文件赋予一个私有作用范围。这是使 require 和 module 模块在本地引用的一种方法;
Evaluating:VM 对加载的代码进行处理的地方;
评论0
最新资源