ps 转html node,nodeJs学习笔记-Node的模块实现

nodeJs学习笔记-Node的模块实现

CommonJS规范

CommonJS 对于模块的定义十分的简单,主要分为模块引用,模块定义和模块标识3分部分。

模块引用

var math = require('math');

模块定义

在模块中,上下文提供require()方法来引入外部模块,对应引入的功能,上下文提供了exports对象用于导出当前模块的方法和变量,并且它是唯一导出的出口。在模块中,还存在一个一个module对象,它代表模块本身,而expirts是module的属性,在Node中,一个文件就是一个模块,将方法挂载在exports对象上作为属性就可以定义导出的方式。

// 方式一

// hello.js

function Hello(){

var name;

this.setName = function(thyName){

name = thyName

}

this.sayHello = function(){

console.log('hello ' + name);

}

}

exports.Hello = Hello;

// main.js

var Hello = require('./hello.js').Hello;

// 方式二

// hello.js

function Hello(){

var name;

this.setName = function(thyName){

name = thyName

}

this.sayHello = function(){

console.log('hello ' + name);

}

}

module.exports = Hello;

// main.js

var Hello = require('./hello.js')

PS:不可以通过对exports直接赋值替代对module,exports赋值。exports实际上只是一个和module.exports指向同一个对象的变量,它本身会在模块执行结束后释放,但是module却不会,因此只能通过指定module.exports来访问该接口

模块标识

模块标识实际上就是传递给require()方法的参数,他必须是符合小驼峰命名的字符串,或者是以.,..开头的相对路径,或者绝对路径。它可以没有文件后缀js

Node的模块实现

Node在实现上面并没有完全按照规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性。

在Node中引入模块,需要经历三个步骤。

路径分析

文件定位

编译执行

在Node中,模块是分为两类的:一类是Node提供的模块,叫核心模块,一类是用户自己编写的模块,叫文件模块。

对于核心模块:核心模块部分在Node源代码的编译过程中,编译进了二进制执行文件。在Node进程启动的时候,部分核心模块就直接被加载到了内存中,所以这部分核心模块引入的时候,文件定位和编译执行这两个步骤可以省略掉了,并且在路径分析中优先判断,所以它的加载速度是最快的。

对于文件模块:文件模块是在运行过程的时候动态加载的,需要完整的路径,文件定位,编译执行过程,速度也是最慢的。

模块的加载过程

优先从缓存加载

Node对于引入过的模块都会进行缓存,以减少二次引入时的开销,不同于浏览器的是,浏览器仅仅缓存文件,而Node缓存的是编译和执行之后的对象。

无论是核心模块还是文件模块,require()方法对于相同模块的二次加载都是一律采用缓存优先的方式,这是第一优先级的,不同之处在于核心模块的缓存检查优先于文件模块的缓存检查。

路径分析和文件定位

模块标识符分析

核心模块:最快

路径形式的文件模块:较快

自定义模块:最慢

文件定位

文件扩展名分析,按照.js、.node、.json的次序补充扩展名,依次尝试。

PS:建议带上扩展名,以防止性能问题

目录分析和包:首先查找package.json文件。如果不存在该文件,则会去找index.js、index.node、index.json

模块编译

在Node中,每个文件模块都是一个对象。

编译和执行是引入文件模块的最后一个阶段,定位到具体的文件之后,Node会新建一个模块对象,然后根据路径载入并编译。对于不同的文件扩展名,其载入方法也是不同的。

.js文件,通过fs模块同步读取文件之后编译执行

.node文件,通过用c/c++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件

.json文件,通过fs模块同步读取文件之后,使用JSON.parse()方法解析返回结果。

其余扩展文件,都是被当作.js文件载入的。

PS:对于每一次编译成功的模块都会将其文件路径作为索引缓存在Module,_cache对象上,一提高二次引入的性能。

javascript模块的编译

在编译的过程中,Node对获取的javascript文件内容进行了头尾包装。在头部添加了(function(exports,require,module,__filename,__dirname){\n,在尾部添加了\n});

(function(exports,require,module,__filename,__dirname){

var math = require('math');

exports.area = function(radius){

return Math.PI * radius * radius

}

});

这样每一个模块文件之间都进行了域隔离。

C/C++模块的编译

Node调用process.dlopen()方法进行加载和执行,在Node框架下,dlopen()方法在window和*nix平台下分别有不同的实现,通过libuv兼容层进行封装。

JSON文件的编译

Node利用fs模块同步读取JSON文件的内容之后,调用JSON.parse()方法获取到对象,然后将它赋值给模块对象的exports,以供外部调用。

Node核心模块

Node的核心模块分为两块:c/c++编写的+javascript编写的

其中c/c++编写的放置在Node项目的src目录下面,javascript编写的放置在lib目录下

javascript核心模块的编译过程

1.转化为c/c++代码

Node采用了V8附带的js2c.py工具,将所有的内置的javascript代码(src/node.js和lib/*.js)转换成c++里面的数组,生成node_natives.h头文件

在这个过程中,javascript代码以字符串的形式存储在node命名空间里,是不可直接执行的。在启动Node进程的时候,javascript代码直接加载进内存中。在加载的过程中,javascript核心模块经历标识分析直接定位到内存中,这个比普通的文件模块从磁盘中一处一处的查找快的多。

2.编译javascript核心代码

lib目录下面的所有模块文件也没有定义require、module、export这些变量。在进入javascript核心模块的过程中,也经历了头尾包装的过程,然后才执行和导出了exports对象。

与文件模块的区别的地方是:获取源码的方式(核心模块是从内存中直接读取的),以及缓存执行结果的位置。

javascript核心模块的定义如下,源文件是通过process.binding('natives')取出,编译成功的模块缓存到NativeModule._cache对象上,文件模块则缓存到Module._cache对象上;

function NativeModule(id){

this.filename = id + '.js';

this.id = id;

this.exports = {};

this.loaded = false;

}

NativeModule._source = process.binding('natives');

NativeModule._cache = {};

c/c++核心模块的编译过程

我们把那些由纯c/c++编写的部分统一称为内建模块,因为它们通常不被用户直接调用。在Node中,buffer, crypto, evals, fs, os等模块都是部分通过c/c++编写的。

内建模块的组织形式

内建模块的导出

c/c++扩展模块

略,讲的太深了,看不懂,以后再慢慢啃吧!

模块调用栈

略,讲的太深了,看不懂,以后再慢慢啃吧!

包与NPM

包结构

包实际上是一个存档文件,即一个目录直接打包为.zip或者tar.gz格式的文件,安装后解压还原为目录。符合CommonJS规范的包目录应该包含下面的文件

package.json:  包描述文件

bin:  用于存放可执行二进制文件的目录

lib:  用于存放javascript代码的目录

doc:  用于存放 文档的目录

test:  用于存放单元测试用例的代码

包描述文件与NPM

略,讲的太深了,看不懂,以后再慢慢啃吧!

前后端公用模块

略,讲的太深了,看不懂,以后再慢慢啃吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值