content
1. node的介绍
js缺陷
- 没有模块系统
- 标准库少(文件系统,i/o等操作没有常见api)
- 没有标准接口
- 缺乏包管理工具
node特点
异步I/O
- 绝大多数的操作都以异步的方式
进行调用
$.post('/url', {params}, function(data){ console.log('receive response') })
事件与回调函数
单线程
- 无法利用多核CPU可以编写c/c++扩展方式
提高CPU利用率
2. 模块机制
在js中没有向python(import), java(import)等语言的引入模块,为解决这些问题,引入了CommonJS规范。
2.1 commonjs规范
希望javascript能在任何地方运行。
出现的原因
- 弥补当前js没有标准的缺陷。(没有模块系统,标准库少,没有标准接口,没有包管理系统)web1.0
- js只有对dom和bom等基本操作。web2.0
- h5的出现,进入web应用时代,有很多api让js调用。提高API供js调用
2.1.1 commenjs的模块规范
2.2 node的模块实现
模块的分类:
-
node提供的
核心模块
- 核心模块包括HTTP,fs, path等
- 部分源代码在编译中,编译为
二进制执行文件
- 在node进行
启动时
,部分核心模块,被加载内存中
- 对于这
部分核心模块
,文件定位
和编译执行
被省略
,在路径分析中有点判断,加载速度最快。
-
用户编写的文件模块
优先从缓存加载
- node对引入过的模块会进行缓存,减少二次开销。node缓存的是编译和执行之后的对象。
引入模块的步骤
:
- 路径分析
- 文件定位
- 编译执行
2.2.1 路径分析
模块标识符
分析,require()方法接受一个标示符作为参数。
const variable = require('模块标示符')
- 核心模块 - 优先级仅次于缓存模块
- 路径路径形式的文件模块 - . …和/的标示符
- 自定义模块 - 非核心模块
console.log(module.paths); // 返回的是数组
// 生成路径的规则
* 当前目录下的node_modules目录
* 父目录下的node_modules目录
* 父目录的父目录下的node_modules目录
* 。。。 依次的地递归
2.2.2 文件定位
依据缓存加载的优先策略使得不需要路径分析、文件定位和编译执行的过程,大大提高再次加载模块时的效率。文件定位工作如下:
文件扩展名分析
- require()分析过程中,会对不包含文件扩展名,node会按照.js , .json , .node次序补足扩展名
。目录分析和包
- 当在分析标示符没有查到文件时,首先,node在当前目录下查找package.json,通过JSON.parse解析出描述对象,从中抽取main的value,进行文件定位。若无package.json文件,则node按照index.js,index.json和index.node文件进行查找
...
"main": "index.js", //在package.json指定main
"module": "es/index.js",
"typings": "types/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/alibaba-fusion/next.git"
}
...
2.2.3 模块编译
node中,每个文件模块都是一个对象
。
关于不同的文件的编译过程如下,查看源代码下,lib/module.js可知道:
.js文件
- 通过fs模块,同步读取文件后编译执行
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
};
.node.js文件
- 这是c/c++的扩展文件,通过dlopen方法加载最后编译生产的文件.
//Native extension for .node
Module._extensions['.node'] = process.dlopen;
.json文件
- 通过fs模块,同步读取,用JSON.parse解析返回结果.
Module._extensions['.json'] = function(module, filename) {
var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
};
//Module._extensions 会被赋值为require()的extension属性
// console.log(require.extensions)
// { '.js': [Function], '.json': [Function], '.node': [Function] }
其余文件
- 当做js文件加载.
核心模块
分为c/c++编写和js编写:
js编写
- 文件存放于lib目录下
- 先转换为c/c++代码,使用v8附带的js2c.py工具,将所以js代码,转换为c++数组里.
- 编译js核心代码,js核心模块通过process.binding(‘natives’)取出,编译成功的缓存模块存到NativeModule._cache对象上,文件模块则缓存在Module._cache对象上.
c/c++编写
- 存在与node的src目录下
分为纯c/c++编写,称为内建模块
。
部分c/c++编写,buffer,crypto,evals,fs,os等模块.
下面讲对内建模块进行说明
:
优点
: 本身是由c/c++编写,性能上优于脚本语言,文件被编译成二进制文件,一般执行,就被加载到内存中,需要标识符定位,文件定位,编译等过程。
编写的内建模块,统一放进node_extensions.h文件中放进node_module_list数组中。
内建模块的依赖关系如下,不推荐直接通过node文件调用内建模块:
[外链图片转存失败(img-hFbE2IDn-1562430409356)(https://s2.ax1x.com/2019/06/22/ZpdXXd.png)]
核心模块os的流程:
- 先通过NOde_module对os的操作进行存储,这里我理解的是key:function(){}形式
- 再通过get_builtin_module(‘node_od’)形式调用
- process.binding(‘os’) 可以通过js调用reg_func函数
- 编译成功的模块缓存到NativeModule_cache对象上,再通过NativeModule.require(‘os’)调用
- 最后就可以在js中使用
[外链图片转存失败(img-1a7hBuGf-1562430409357)(https://s2.ax1x.com/2019/06/22/Zpwa4K.png)]