当前位置:网站首页>node中引入模块的原理
node中引入模块的原理
2022-07-01 06:29:00 【song854601134】
文章目录
1. 模块机制
1.1 commonjs规范
- CommonJS规范为JavaScript制定了一个美好的愿景——希望JavaScript能够在任何地方运行。
- CommonJS对模块的定义十分简单,主要分为模块引用、模块定义和模块标识3个部分。
- 模块引用:通过require方法引入模块
var math = require('math');
- 模块定义:通过exports对象导出当前模块的方法或变量(exports是module的属性)
exports.add = function() {
}
- 模块标识:就是传递给require方法的参数,可以是以下几种形式(可以没有文件后缀名.js):
- 符合小驼峰命名的字符串
- 以.、…开头的相对路径
- 绝对路径
1.2 node的模块实现(node中引入模块的过程)
node在实现中并非完全按照connonjs规范,而是对模块规范进行了一定的取舍,同时增加了少许自身的特性。
1. 在node中引入模块需要经历如下3个步骤:
- 路径分析
- 文件定位
- 编译执行
2. node中,模块分为以下两类:
- 核心模块:node提供的模块
- 核心模块在node源代码编译过程中,编译进了二进制执行文件
- 在node进程启动时,部分核心模块就被直接加载到了内存中(所以这部分核心模块在引入时,文件定位和编译执行这两部分是可以省略的,且在路径分析中优先判断,加载速度最快)
- 文件模块:用户编写的模块
- 需要在运行时动态加载,需要完整的路径分析、文件定位和编译执行。速度较慢
以下为详细的模块加载过程:
1.2.1 优先从缓存中加载
- node对引入过的模块都会进行缓存,以减少二次引入时的开销(和浏览器缓存的不同之处在于:浏览器仅仅缓存文件,而node缓存的是编译和执行后的对象)
- 对核心模块的缓存检查优先于文件模块
1.2.2 路径分析
因为标识符有几种形式,所以对于不同的标识符,模块的查找和定位有着不同程度的差异。模块分类如下:
- 核心模块:如http、fs、path等
- 以.或…开始的相对路径文件模块
- 以/开始的绝对路径文件模块
- 非路径形式的文件模块,如自定义的文件模块
1. 核心模块
核心模块的优先级仅次于缓存加载,它在node源代码编译过程中被编译成了二进制文件,加载速度最快。
2. 路径形式的文件模块
require会将路径转化为真实路径,并以真实路径作为索引,将编译执行后的结果放到缓存中,以使二次加载更快。
3. 自定义模块
node会逐个尝试模块路径中的路径,直到找到目标文件为止。
- 模块路径概念:模块路径是node在定位文件模块的具体文件时指定的查找策略,具体表现为一个路径组成的数组(可通过module.paths输出)
- 模块路径的生成规则:
- 当前文件目录下的node_modules目录
- 父目录下的node_modules目录
- 父目录的父目录下的node_modules目录
- 沿路径向上逐级递归,直到根目录下的node_modules目录
1.2.3 文件定位
- 文件扩展名的分析
- 目录和包的处理
1. 文件扩展名的分析:当传递给require的标识符不包括文件扩展名时,node会按照以下次序补足扩展名,依次尝试:
- .js
- .json
- .node
在尝试的过程中,node会调用fs模块同步阻塞式的判断文件是否存在。因为node是单线程的,所以这里是一个会引发性能问题的地方。(解决:在引入.json和.node文件时,加上扩展名)
2. 目录和包的处理:require通过分析文件扩展名后,可能得到的是一个目录,此时node会将目录当做一个包来处理。
- 首先,node在当前目录下查找package.json。通过json.parse解析出包描述对象,从中取出main属性指定的文件名进行分析,若缺少扩展名,则会进入扩展名分析的步骤。
- 若main指定的文件名错误,或者没有package.json,那么node会将index作为文件名。然后依次查找index.js、index.json、index.node
- 如果在目录分析的过程中没有成功定位到任何文件,则自定义模块会进入到下一个模块路径继续查找。若遍历完模块路径后仍然没有,则会抛出查找失败的异常。
1.2.4 模块编译
定位到具体的文件后,node会新建一个模块对象,然后根据路径载入和编译。对于不同的扩展名,其载入方式如下:
- .js文件:通过fs模块同步读取文件后编译执行
- .node文件:这是用c/c++编写的扩展文件,通过dlopen加载最后编译生成的文件
- .json文件:通过fs模块同步读取文件后,用Json.parse解析返回结果
- 其余扩展名文件:都被当做.js文件载入
注:每个编译成功的模块,都会将其文件路径作为索引缓存在Module._cache对象上,以提高二次引入的性能
1. js模块的编译
node对获取的js文件进行了头尾的包装,包装后的代码会通过vm原生模块的runInThisContext()执行
(function(exports, requiure, module, __filename, __dirname){
...
}
2. c/c++模块的编译
node调用process.dlopen()进行加载和执行,(dlope在windows和*nux平台通过libuv进行了兼容).node模块并不需要编译,因为他是编写c/c++模块之后编译生成的。
3. json文件的编译
- 通过fs模块同步读取到json文件的内容
- 通过JSON.parse()得到对象
- 复制给module.exports
1.3 核心模块
- c/c++编写(存放在node的src目录)
- js编写(存放在lib目录)
1.3.1 js核心模块的编译过程
- 转存为c/c++代码:node通过v8附带的js2c.py工具,将所有内置的js代码转换成c++里的数组,生成node_natives.h头文件。(在这个过程中,js代码以字符串的形式存储在node命名空间中。在启动node进程时,js代码直接加载进内存中)
- 编译js核心模块:也经历了头尾的包装过程(与文件模块的区别在于:获取源码的方式和缓存执行结果的位置)
- 核心模块从内存中加载
- 编译成功的模块缓存到NativeModule._cache对象; 文件模块则缓存到Module._cache对象
1.3.2 c/c++核心模块的编译过程
- 内建模块:全部由c/c++编写(如buuffer、fs、os等)
- c/c++完成核心部分,js实现封装
1. 内建模块
- 每一个内建模块在定义之后,都通过NODE_MODULE宏将模块定义到node命名空间中。
- nodex_extensions.h头文件将这些散列的内建模块统一放到了node_module_list数组
- node提供了get_builtin_module()从node_module_list中取出内建模块
- 内建模块的导出:通过process.binding()来加载内建模块(process是node在启动时生成的全局变量)
1.3.3 核心模块的引入流程
- NODE_MODULE(node_os, reg_func)
- get_builtin_module(‘node_os’)
- process.binding(‘os’)
- NativeModule.require(‘os’)
- require(‘os’)
1.4 c/c++扩展模块
- 模块编写:
普通的扩展模块和内建模块的区别在于:无需将源代码编译进node,而是通过dlopen()动态加载
模块编译:通过gpy工具
模块加载:通过require()加载
对于.node文件使用process.dlopen()加载
- 通过uv_dlopen()打开动态链接库
- 通过uv_dlsym()找到动态链接库中通过NODE_MODULE宏定义的方法地址
(注:以上两个方法都是在libuv库中实现的。在不同的操作系统下分别调用不同的方法来分别加载.node在该操作系统下对应的文件.so和.dll)
边栏推荐
猜你喜欢
随机推荐
C language course design student information management system (big homework)
H5网页判断是否安装了某个APP,安装则跳转未安装则下载的方案总结
[ManageEngine] terminal management system helps Huasheng securities' digital transformation
2022 Jiangsu Vocational College skills competition (secondary vocational school) network construction and application open competition volume
自开发软件NoiseCreater1.1版本免费试用
On whether variables are thread safe
C语言课设学生信息管理系统(大作业)
Mongodb: I. what is mongodb? Advantages and disadvantages of mongodb
Code power is full of quantitative learning | how to find a suitable financial announcement in the financial report
json模块
B-tree series
Find the original array for the inverse logarithm
启牛学堂合作的证券公司是哪家?开户安全吗?
How did ManageEngine Zhuohao achieve the goal of being selected into Gartner Magic Quadrant for four consecutive years?
Async and await
[summary of knowledge points] chi square distribution, t distribution, F distribution
[unity shader ablation effect _ case sharing]
伪装请求头库: anti-useragent
根据输入画有向图
[wechat applet low code development] second, resolve the code composition of the applet in practice








