站长家园(原代码之家)(www.adminjie.com)网站源码,微信源码,游戏源码,商业源码分享平台。
当前位置:网站首页 技术文章 网络编程 正文

深入了解Node的模块机制,聊聊模块实现流程

时间:2022-05-27 [网络编程]作者:fabuyuan 浏览:5 次

本篇文章带大家了解一下CommonJs规范和Node的模块机制,介绍一下Node实现CommonJs规范的基本流程,希望对大家有所帮助!

在CommonJs规范提出之前,Javascript是没有模块系统的,这意味着我们很难开发大型的应用,因为代码的组织会比较困难。

什么是CommonJs规范


首先CommonJS不是Node独有的东西,CommonJs是一种模块规范,定义了如何引用和导出模块,Nodejs只是实现了这个规范,CommonJS模块规范主要分为模块引用、模块定义和模块标识三个部分。

模块引用

模块引用就是我们可以通过require引入其它的模块。

const { add } = require('./add');
const result = add(1 ,2);

模块定义

一个文件就是一个模块,模块里会提供两个变量,分别为module和exports。module为当前模块本身,exports为要导出的内容,同时exports为module的一个属性,即exports为module.exports。其他模块通过require导入的内容即为module.exports的内容。

// add.js
exports.add = (a, b) => {
    return a + b;
}

模块标识

模块标识即为require里面的内容,比如require('./add'),则模块标识为./add

通过CommonJS构建的这套模块导入导出机制使得用户完全无需考虑变量污染,可以方便的构建大型应用。

Node的模块实现


Node实现了CommonJs规范,并且增加了一些自己需要的特性。Node为了实现CommonJs规范主要做了以下三件事情:

  • 路径分析

  • 文件定位

  • 编译执行

路径分析

当执行require()的时候,require接收的参数即为模块标识符,node通过模块标识符来进行路径分析。路径分析的目的就是为了通过模块标识符找到这个模块所在的路径。首先,node的模块分为两类,分别是核心模块和文件模块。核心模块是node自带的模块,文件模块是用户编写的模块。同时文件模块又分为相对路径形式的文件模块、绝对路径形式的文件模块和非路径形式的文件模块(比如express)。

深入了解Node的模块机制,聊聊模块实现流程

当node找到一个文件模块之后,会将这个模块编译执行并且缓存起来,大致原理是将这个模块的完整路径作为key,编译后的内容作为值,后续再第二次引入这个模块的时候就不需要再进行路径分析文件定位编译执行这几个步骤了,可以直接从缓存中读取编译好的内容。

// 缓存的模块示意:
const cachedModule = {
    '/Usr/file/src/add.js': 'add.js编译后的内容',
    'http': 'Node自带的http模块编译后的内容',
    'express': '非路径形式自定义文件模块express编译后的内容'
    // ...
}

当要查找require导入的模块时,查找模块的顺序是先查看缓存里是否已经有该模块,如果缓存里面没有再查看核心模块,然后再查找文件模块。其中路径形式的文件模块比较好查找,根据相对或绝对路径就可以得到完整的文件路径。非路径形式的自定义文件模块查找起来会相对麻烦一些,Node会从node_modules这个文件夹里去查找是否有这个文件。

node_modules这个目录在哪里呢,比如说我们当前执行的文件为/Usr/file/index.js;

/** 
* /Usr/file/index.js;
*/

const { add } = require('add');
const result = add(1, 2);

这个模块里我们有引入了一个add模块,这个add不是一个核心模块也不是一个路径形式的文件模块,那么这时候如何找到这个add模块呢。

module有一个paths的属性,查找add模块的路径在paths这个属性里,我们可以把这个属性打出来看一下:

/** 
* /Usr/file/index.js;
*/

console.log(module.paths);

我们在file目录下执行node index.js可以打印出paths的值。paths里的值是一个数组,如下:

[
'/Usr/file/node_modules',
'/Usr/node_modules',
'/node_modules',
]

即Node会依次从上面的目录里寻在是否包含add这个模块,原理和原型链类似。先从当前执行的文件的同级目录的node_modules文件夹里开始找,如果没找到或者没有node_modules这个目录,则继续往上级查找。

文件定位

路径分析和文件定位是搭配一起使用的,文件标识符可以是不带后缀的,也可能通过路径分析找到的是一个目录或者一个包,这个时候要定位到具体的文件需要一些额外的处理。

文件扩展名分析

const { add } = require('./add');

比如上面这段代码,文件标识符是不带扩展名的,这个时候node会依次查找是否存在.js、.json、.node文件。

目录和包分析

同样是上面这段代码,通过./add查找到的可能不是一个文件,可能是一个目录或者包(通过判断add文件夹下是否有package.json文件来判断是目录还是包)。这个时候文件定位的步骤是这样的:

  • 查看是否有package.json文件
      • 读取package.json里的main字段的值作为文件
    • 没有
      • 寻找目录下的index作为文件(依次查找index.js、index.json、index.node)

如果package.json里没有main字段,那么也会将index作为文件,然后进行扩展名分析找到对应后缀的文件。

模块编译

我们开发中主要遇到的模块为json模块和js模块。

json模块编译

当我们require一个json模块的时候,实际上Node会帮我们使用fs.readFilcSync去读取对应的json文件,得到json字符串,然后调用JSON.parse解析得到json对象,再赋值给module.exports,然后给到require。

js模块编译

当我们require一个js模块的时候,比如

// index.js
const { add } = require('./add');
// add.js
exports.add = (a, b) => {
    return a + b;
}

这个时候发生了什么呢,为什么我们可以直接在模块里使用module、exports、require这些变量。这是因为Node在编译js模块的时候对模块的内容进行了首尾的包装。

比如add.js这个模块,实际编译的时候是会被包装成类似这样的结构:

(function(require, exports, module) {
  exports.add = (a, b) => {
    return a + b;
  }
  return module.exports;
})(require, module.exports, module)

即我们编写的js文件是会被包装成一个函数,我们编写的只是这个函数里的内容,Node后续的包装的过程对我们隐藏了。这个函数支持传入一些参数,其中就包括require、exports和module。

当编译完js文件后,就会执行这个文件,node会将对应的参数传给这个函数然后执行,并且返回module.exports值给到require函数。

以上就是Node实现CommonJs规范的基本流程。

更多node相关知识,请访问:nodejs 教程!

以上就是深入了解Node的模块机制,聊聊模块实现流程的详细内容,更多请关注站长家园其它相关文章!

本文标签:  nodejs​Node.jsnode

转载请注明来源:深入了解Node的模块机制,聊聊模块实现流程

本文永久链接地址:https://www.adminjie.com/post/12854.html

免责声明:
本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

附:
二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!

版权声明:
一、本站致力于为软件爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的部分源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、我站提供用户下载的所有内容均转自互联网。如有内容侵犯您的版权或其他利益的,若有侵犯你的权益请:提交版权证明文件到邮箱 2225329873#qq.com(#换为@) 站长会进行审查之后,情况属实的会在三个工作日内为您删除。

  • 站长家园(原代码之家)会员升级
  • 最新文章
    • php怎么去除二维数组中的第一个子数组

      php怎么去除二维数组中的第一个子数组

      去除方法:1、用foreach遍历二维数组的外层数组元素,语法“foreach($arras$k=>$v){//循环体}”;2、在循环体,使用is_a...

    • html5中web储存的含义是什么

      html5中web储存的含义是什么

      html5中web储存的含义是让网页在用户计算机上保存一些信息,使用HTML5可以在本地存储用户的浏览数据;web储存又可以分为本地存储和会话存储,分别对应lo...

    • html5的注释有快捷键吗

      html5的注释有快捷键吗

      html5的注释有快捷键;可以利用“ctrl+/”快捷键实现注释,选中需要注释的代码后,利用该快捷键就会在指定代码前添加“<!--”,在指定代码后添加“-...

    • mysql索引的查询语句是什么

      mysql索引的查询语句是什么

      mysql索引的查询语句是“SHOWINDEX”,可以返回与当前数据库或指定数据库中的表关联的索引信息,完整语法“SHOWINDEXFROM表名[FR...

    • html5里面的data属性的作用是什么

      html5里面的data属性的作用是什么

      html5里面的data属性的作用是存储私有页面后应用的自定义数据,“data-*”是HTML5新增的一个自定义数据属性功能,作为可以暂存数据使用,自定义的数据...

    热门文章