nodejs模块机制

模块机制

CommonJs规范

  1. 模块引用
    上下文提供require()方法来引人外部模块var math = require('math')

  2. 模块定义

    • exports 对象用于到处当前模块中的方法和变量
    • module代表模块自身
    exports.add = function() {...}
    
    • 在另一个模块中使用require()方法进行导入。就可以使用
    区别和联系
    module对象: 在每个js自定义模块中都存在module对象。
        Module {
            id: '',
            path: '',
            exports: {},
            ...
        }
    module.exports: 在module对象中,可以使用module.exports来进行共享
    exports是modeule.exports的简写
    

    在这里插入图片描述

  3. 模块标识
    模块标识其实就是传递给require()方法的参数,它必须是符合小驼峰命名的字符串,或者…开头的相对路径,或者绝对路径。可以没有文件名后缀.is。
    将类聚的方法和变量等限定在私有的作用域中,同时支持引人和导出功能以顺畅地连接上下游依赖。(如上图所示)

  4. node 的模块实现

    • 在node中引入模块,需要经历路劲分析,文件定位,编译执行。
    1. 文件分类

      • 核心模块
        核心模块部分在node源代码的编译过程中,编译进了二进制执行文件。在node进程启动的时候,部分核心模块被加载进入内存中,所以这部分模块引入到时候,文件定位和编译执行这个两个步骤可以省略掉,在路径分析中优先判断。加载速度是最快的。
        在核心模块中,有些模块由c/c++进行编写,有的模块由c/c++完成核心部分。其他的部分由JavaScript实现包装或者向外导出。以满足性能需求。c++模块主内完成核心,JavaScript主外实现封装是node能够实现高性能的常见方式。
      • 文件模块
        文件模块是在运行的时候动态加载,需要完整的路劲分析,文件定位,编译执行过程,速度比核心模块慢。
        在这里插入图片描述
    2. 优先从缓存加载

      • node对于引入过的模块回进行缓存。减少二次引入的开销。浏览器只是缓存文件,但是node缓存的是编译和执行后的对象。
        3. 路径分析和文件定位
        1. 模块标识符分析
        dotnetcli node中的模块标识符 1. 核心模块,如http,fs,path 2. . 或者是..开始的相对路径文件模块 3. 以/开始的绝对路劲文件模块 4. 非路径形式的文件模块。 以. 或者..或者/开始的标识符,被当作文件模块来处理,分析路劲模块的时候,require()会将路径转换为真实路径,并且以真实路径作为索引。将编译执行后的结果放到缓存中,使得二次加载的时候更快。
        2. 模块路径
        + 模块路径是node在定位文件规模的具体文件的时候,指定的查找策略,具体表现为一个路径组成的数组。
        + 模块路径的生成规则:在加载过程中,node会逐个尝试模块路径中的路径,知道找到目标文件为止。当前文件的路径越深,模块加载耗时越多。自定义模块在速度是最慢的
        3. 文件定位
        1. 文件拓展名分析
        在分析标识符的时候,会出现标识符不包含文件拓展名的情况,正常情况下,node会按照js json node的顺序补足拓展名
        在尝试为文件加上后缀的时候,需要调用fs模块同步阻塞是判断文件是否存在,提升性能: node和json文件,在传递个require的时加上后缀名。
        2. 目录分析和包
        1. node在当前目录下查找package.json(commonjs规范的包描述文件)
        2. 通过json.parse()解析出包描述文件,从中取出main属性指定的文件名进行定位
        3. 如果文件名缺少拓展,会进入拓展名分析步骤 、
        4. 如果main属性指定的文件名错误,或者压根没有package.json文件,node会将index当作默认文件名。然后依次查找index.js, index.json, index.node
    3. 模板编译

      // 模板对象
      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 = [];
      }
      

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

      js: fs模块
      node: dlopen()
      json: fs模块=> JSON.parse()
      其余: 当作js文件
      

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

       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()的extensions属性,在代码中require.extensions可以知道系统的已有的加载方式
      如果想对自定义的托张敏进行特殊的加载,可以通过类似的require.extensions[‘.ext’]的方式来实现。官方建议将文件编译成js文件以后再进行执行

      1. JavaScript模板的编译
        1. 编译的过程中,node对获取的JavaScript文件内容进行了头尾包装,在头部添加了(function (exports, require, module, __filename, __dirname) {\n 再尾部添加了\n}) 一个正常的JavaScript文件背包装成如下的样子
          (function (exports, require, module, __filename, __dirname) {
              var math = require('math')
              exports.area = function(radius) {
                  return Math.PI * radius * radius
              }
          })
          
          这样的每个模块文件之间都进行了作用域隔离,包装之后的代码会通过vm原生模块的runInThisContext()方法进行执行,类似于eval,有明确的上下文,不会污染环境,返回一个具体的function对象,最后将当前的模块对象的exports属性,requires方法,module以及再文件定义中得到的完整文件路径喝文件目录作为参数传递给这个function()执行
          这些变量虽然没有再模块文件中定义,却会在文爱中存在。执行之后,exports会被返回给调用方。exports属性上的任何方法属性都可以外部调用到,但是模块中的其余变量或属性不可以杯直接调用
          • 理想情况: 只需要赋值给exports对象。如果需要达到require引入一个类的结果,赋值给module.exports对象。
      2.  c/c++
          1.  使用通过process.dlopen()方法进行加载和执行。再node架构下,dlopen方法再window和方法下有不同的实现。
          2.  node的模块文件不需要编译,node文件实在编写c/c++之后编译生成的。然后返回给调用值。
      3. JSON文件的编译
         1. json的文件的编译是3种编译方式中最简单的。node利用fs模块同步读取json文件之后,调用json.parse()方法获得对象。然后将他赋值给对象的exports。如果定义了一个json文件作为配置。就不需要调用fs模块去异步读取或者编译,直接调用require()引入就可以。
      
  5. 模块间的调用关系
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/552439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电力系统卫星授时信号安全隔离装置防护方案

电力系统是国家关键基础设施, 电力安全关系国计民生, 是国家安全的重要保障, 与政治安全、经济安全、 网络安全、社会安全等诸多领域密切关联。电网运行情况瞬息万变,为了在其发生事故时能够及时得到处理,需要统一的时…

Redis中的事务(二)

事务 一个完整的WATCH事务执行过程 假设当前服务端为c10086,而数据库watched_keys字典的当前状态如图所示,那么当c10086执行以下WATCH命令之后 c10086> WATCH "name" OKwatched_keys字典将更新如图所示的状态。接下来客户端c10086继续向…

[2021最新]Java时间戳和日期时间互转换

代码: import java.text.ParseException; import java.text.SimpleDateFormat;public class MainProcess {public static void main(String[] args) throws ParseException {// 1.set formatSimpleDateFormat timeSmat new SimpleDateFormat("yyyy-MM-dd HH:…

Kubernetes中安装部署ActiveMQ集群(手把手式记录)

目录 1、创建命名空间 nacos-cluster 2、配置文件准备 2.1 activemq0.xml 2.2 activemq1.xml 2.3 activemq2.xml 3、创建configMap cm-activemq 4、创建activemq-cluster.yaml 5、执行命令部署 6、部署成功,查看结果 这里以3个borker的集群为例&#xff0…

Facade 外观

意图 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一字系统更加容易使用。 结构 其中: Facade知道哪些子系统负责处理请求;将客户的请求代理给适当的子系统对象。 Subsystem classes…

QGIS插件Geo-SAM使用(基于SAM半自动标注遥感图像)

0.Geo-SAM介绍 Geo-SAM是一个QGIS插件,旨在帮助人们在使用大尺寸地理空间栅格图像时有效地分割、描绘或标记地貌。Segment Anything Model (SAM) 是一个具有超能力的基础 AI 模型,但模型大小巨大,即使使用现代 GPU&am…

C++学习进阶版(一):用C++写简单的状态机实现

目录 一、基础知识 1、状态机 2、四大要素 3、描述方式 4、设计步骤 5、实现过程中需注意 (1) 状态定义 (2) 状态转换规则 (3) 输入处理 (4) 状态机的封装 (5…

Nginx第2篇-HTTPS配置教程

背景 我最近做个项目要上线,接口部署到服务器,总不能给别人个ip地址加端口吧,而且小程序上线要有接口不能是ip和http协议,必须是https协议。这里记录下使用Nginx配置HTTPS的过程,主要包含以下三部分。 申请域名SSL证…

远程预付费集抄管理系统

远程预付费集抄管理系统是一种用于能源(如水、电等)预付费管理的智能化系统,其核心在于提供远程集中抄表和费用管理服务。这种系统通过集成先进的远程监控技术和预付费管理功能,为用户提供了便捷的能源管理解决方案。下文将从核心功能、工作流程、优势特…

离世界模型更近一步!Meta开源OpenEQA,评估AI Agent情景理解能力

Yann LeCun 朝着 “世界模型” 又近了一步。 Meta最新的开源工作OpenEQA:从文字模型到世界模型,可以像人一样记忆、推理的新基准,AI理解物理空间又近了一步。 场景1: 假设你正准备离开家,但找不到你的工牌。 现在,…

5.2 iHRM人力资源 - 员工管理 - 使用文件导入导出员工

iHRM人力资源 - 员工管理 - 导入导出员工 文章目录 iHRM人力资源 - 员工管理 - 导入导出员工一、员工导出Excel二、员工导入Excel2.1 Excel导入组件封装2.2 下载导入模板2.3 Excel 导入功能 三、删除员工 一、员工导出Excel 这个地方涉及一个接口二进制流blob 就是下面这一大片…

使用嘉立创EDA打开JSON格式的PCB及原理图

一、将PCB和原理图放同一文件夹 并打包成.zip文件 二、打开嘉立创EDA并导入.zip文件 文件 -> 导入 -> 嘉立创EDA标准版/专业版 三、选择.zip文件并选择 “导入文件并提取库” 四、自定义工程路径 完成导入并转换为.eprj文件 五、视频教学 bilibili_使用立创EDA打开JSO…

香港科技大学广州|数据科学与分析学域硕博招生宣讲会—华东师范大学专场

时间:2024年4月25日(星期四)13:30 地点:华东师范大学普陀校区文附楼507 报名链接:https://www.wjx.top/vm/Q0cKTUI.aspx# 跨学科研究领域 *数据驱动的人工智能和机器学习 *统计学习和建模 工业和商业分析 *特定行业…

float实现文字环绕效果

实现效果如下: 一、问题分析 接到需求就是右侧显示图片,左侧显示一个标题和内容。第一时间没有想到其他的布局的好的实现方式,就想到了float布局。于是乎去查了下有关float的文档,float 是相当的好用。 float定义如下&#xf…

kibana源码编译

一、安装nodejs16.14.2及yarn (一)nodejs 1、下载 https://cdn.npmmirror.com/binaries/node/v16.14.2/node-v16.14.2-linux-x64.tar.gz2、解压 tar -zxf node-v16.14.2-linux-x64.tar.gz -C /app cd /app mv node-v16.14.2-linux-x64 node3、配置环…

在Linux系统中设定延迟任务

一、在系统中设定延迟任务要求如下: 要求: 在系统中建立easylee用户,设定其密码为easylee 延迟任务由root用户建立 要求在5小时后备份系统中的用户信息文件到/backup中 确保延迟任务是使用非交互模式建立 确保系统中只有root用户和easylee用户…

Matlab|基于改进遗传算法的配电网故障定位

目录 1 主要内容 2 部分代码 3 部分程序结果 4 下载链接 1 主要内容 该程序复现文章《基于改进遗传算法的配电网故障定位》,将改进的遗传算法应用于配电网故障定位中, 并引入分级处理思想, 利用配电网呈辐射状的特点, 首先把整个配电网划分为主干支路和若干独立…

2024年阿里云4核8G配置云服务器价格低性能高!

阿里云4核8G服务器租用优惠价格700元1年,配置为ECS通用算力型u1实例(ecs.u1-c1m2.xlarge)4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选,CPU采用Intel(R) Xeon(R) Platinum处理器,阿里云优惠 aliyunfuwuqi…

【Python】高级进阶(专版提升3)

Python 1 程序结构1.1 模块 Module1.1.1 定义1.1.2 作用1.1.3 导入1.1.3.1 import1.1.3.2 from import 1.1.4 模块变量1.1.5 加载过程1.1.6 分类 1.2 包package1.2.1 定义1.2.2 作用1.2.3 导入1.1.3.1 import1.1.3.2 from import 2 异常处理Error2.1 异常2.2 处理 3 迭代3.1 可…

TinyEMU源码分析之访存处理

TinyEMU源码分析之访存处理 1 访存指令介绍2 指令译码3 地址转换3.1 VA与PA3.2 VA转PA 4 判断地址空间范围5 执行访存操作5.1 访问RAM内存5.2 访问非RAM(设备)内存 6 访存处理流程图 本文属于《 TinyEMU模拟器基础系列教程》之一,欢迎查看其…