common.js模块
module:代表模块自身
exports:是module的属性,用于导出当前模块的方法和属性
require:用于接收一个模块标识引入一个模块
nodejs中模块实现
node引入模块步骤
1)路径分析
2)文件定位
3)编译执行
node模块分类
1)核心模块:Node提供的模块,在node源代码编译过程中,编译成了二进制文件,在Node进程启动时,部分模块被存在了内存中,所以在引入的时候文件定位和编译执行两个步骤会被省略,而且路径分析也是优先分析,所以这类模块是加载最快的
2)文件模块:用户编写的模块,它是动态加载,路径分析,文件定位,编译执行缺一不可,所以没有核心模块加载的快
缓存
和浏览器缓存相似,node也会对引入过的文件进行缓存,但是缓存的是require模块编译和执行过的文件,不论是核心模块还是文件模块都会优先从缓存中读取文件,核心模块的缓存检查优先于文件模块
路径分析
1)核心模块:如fs,http这类模块加载的速度仅次于缓存加载
2)路径形式文件模块,比如以 ./ / ../开头的模块标识符,在分析路径形式的文件模块时,先将其转换为真实路径,并以此路径作为索引将编译执行后的结果存放在内存中,以使第二次的加载时间更快。由于指定了模块的具体文件路径,节省了大量时间,其加载速度慢于核心模块
3)自定义模块:既非核心模块又非路径形式的文件模块。这类模块查找起来最费时间,(1)当前文件的node_modules (2)父文件夹的node_modules (3)。。。 (4)根目录的node_modules 文件夹路径越深,查找的时间越长,直到找到文件为止
文件定位
1)文件扩展名分析:标识符中不包含文件扩展名,则通过.js .json .node来依次进行匹配。在尝试的过程中,会通过同步阻塞的方式来判断文件是否存在,会造成性能问题。如果扩展名为.json或.node加上后缀名会提高效率
2)目录分析和包:若文件扩展名分析过程中未分析到文件而分析到了文件夹,则会将这个文件夹当做包来处理
(1)查找目录packge.json,通过JSON.parse从中取出main属性指定的文件名进行定位,若文件名没有文件扩展名则进入文件名分析阶段
(2)如果main属性指定的文件名错误或package.json文件不存在,Node会将index作为文件名,分别寻找.js .json .node文件
(3)若没有定位到文件,则进入下一个模块路径进行寻找,若所有的模块路径数组遍历完都未寻找到,则抛出异常
模块编译
在Node中,每个文件模块都是一个变量,变量定义如下
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent if(parent && parent.children) { parent.children.push(this) } this.filename = null this.loaded = false this.children = [] } |
在前两步定位到文件之后,会为该文件创建一个模块对象,根据定位到的路径载入文件,载入方式
.js 通过fs模块同步读取文件后编译文件
.node这是用c/c++编写的扩展文件,可以通过dlopen方法载入编译后的文件
.json 通过fs模块同步解析文件后,通过JSON.parse解析返回结果
其余扩展名,都会被当作.js引入
对js的编译,一个正常的js文件,会被如下函数包括,然后runInThisContext(类似于eval),返回一个函数,然后通过将当前模块的exports,require,当前模块自身,当前文件名称,当前文件目录传入函数进行执行,然后将exports对象返回给调用者
(function(exports, require, module, __filename, __dirname) {}) |
包与npm
common包规范:包结构,包描述
包结构:
package.json
lib
bin
test
doc
包描述:package.json