commonjs和nodejs,commonjs和node
node.js速度课程简介:进入学习
Node的应用是由模块组成的,其模块体系借鉴了CommonJS模块规范,但并没有完全按照规范实现,而是根据自身需要增加了一些特性,可以看作是CommonJS模块规范的变种。
CommonJS概述
CommonJS是社区提出的JavaScript模块化规范,可以说是JS模块化历程中最重要的里程碑。它构造了一个——JS可以在任何地方运行的美好愿景,但实际上由于其模块是同步加载的,所以只适用于服务器等其他本地环境,不适合浏览器等需要异步加载资源的地方。
为了让JS可以在任何地方运行,CommonJS制定了一些接口规范,涵盖了模块、二进制、缓冲区、字符集编码、I/O流、进程环境、文件系统、socket、单元测试、web服务器、网关、包管理等。虽然大部分都处于草案阶段,但是已经深深影响了Node的发展。
下图是节点和浏览器,W3C,CommonJS和ECMAScript的关系,取自《深入浅出NodeJS》。
CommonJS的模块规范
CommonJS的模块主要由模块引用、模块定义、模块标识三部分组成。
模块标识
每个模块的模块ID都是唯一的,这是其引用的基础。它必须是以小峰命名的字符串,或者是文件的相对路径或绝对路径。
Require(fs)//fs是内置模块,执行时会直接加载到内存中,没有路径标识。
要求(。/moduleA)//导入当前目录的moduleA
要求(./moduleB)//导入上一个目录的moduleB
Require(C://moduleC)//绝对路径导入moduleC模块引用
使用require()来引用模块。该方法接受模块ID作为参数,以便将模块的API引入到当前上下文中。
Const fs=require(fs)//引入内置fs模块模块定义
进口和出口都有。要将当前上下文中的方法或变量导出为模块,需要使用内置的module.exports对象,这是模块导出的唯一出口。
CommonJS规范规定,在每个模块内部,模块变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是一个外部接口。加载一个模块实际上就是加载模块。
//moduleA.js模块
设moduleA={
名称:“moduleA”
}
模块.导出={
模块a
}
//moduleB.js模块
//导入模块a
const {modulea}=require()的功能。/modulea’)常见的模块如下:
每个模块都有独立的上下文,模块中的代码独立执行,不会污染全局范围。一个模块可以多次加载,但只会在第一次运行,运行结果会被缓存。后续重新加载同一个模块会直接读取缓存的结果,加载module.cache中缓存的模块会按照代码顺序执行。
Node的模块实现
节点导入模块需要经过三个步骤:路径分析-文件定位-编译执行:
路径分析:根据模块标识分析模块类型。
文件位置:根据模块类型和模块标识符找到模块的位置。
编译执行:将一个文件编译成机器代码来执行,这需要一系列的转换。
[推荐研究:《nodejs 教程》]
模块分为内置模块和用户模块:
内置模块:内置模块由node提供,已经编译成二进制可执行文件。Node执行时,内置模块会直接加载到内存中,所以我们可以直接引入。它的加载速度非常快,因为它不需要经过文件定位和编译来执行这两个步骤。
文件:用js或C写的扩展模块,需要先编译成二进制机器码。它需要经过以上三个步骤。
模块缓存
无论是内置模块还是文件模块,node都会在第一次加载后缓存结果。下次加载同一个模块时,会先从缓存中搜索。如果可以找到,将直接从缓存中读取。缓存的结果是模块编译执行后的对象,是所有模块中速度最快的。
路径分析
路径分析基于模块标识符,模块标识符有以下类型:
内置模块标识符,如fs、path等。不需要编译,节点运行时直接加载到内存中导入。相对路径模块ID:使用相对路径描述的文件模块的绝对路径模块ID:使用绝对路径描述的文件模块的自定义模块ID:通常是node_modules中的一个包,导入时不需要写路径描述。Node有一套算法可以找到它,是所有模块id中分析速度最慢的。文件定位
文件定位主要包括文件扩展名分析、目录和包处理。如果在文件定位结束时没有找到文件,将抛出文件查找失败异常。
文件扩展名分析
由于可以在没有文件扩展名的情况下添加模块标识符,节点将按照以下顺序补充扩展名。js,json和。要尝试加载的节点。在尝试加载的过程中,需要调用fs模块来判断文件是否同步存在并被阻塞。所以为了提高性能,可以使用require()导入参数中带有文件扩展名的模块,这样会加快文件定位的速度。
目录、包的处理
当分析文件扩展名时,您可能会得到一个目录。此时Node会把它当作一个包,使用查找包的规则进行查找:在当前目录下查找package.json,获取其中定义的main属性指定的文件名,作为搜索入口。如果没有package.json,则默认设置目录中index的当前默认文件名,然后依次搜索index.js、index.json、index.node。
编译执行
编译和执行是模块导入的最后一步。节点将首先创建一个模块实例来表示当前模块。它具有以下属性:
module.id模块的标识符,通常是带有绝对路径的模块文件名。具有绝对路径的module.filename模块的文件名。Module.loaded返回一个布尔值,该值指示模块是否已完成加载。Module.parent返回一个对象,该对象表示调用该模块的模块。Module.children返回一个数组,表示此模块要使用的其他模块。module.exports表示模块外部输出的值。通过文件位置获得的信息,Node重新加载文件并进行编译。对于不同的文件扩展名,加载方法是不同的:js文件:文件由fs模块同步读取,然后编译执行。节点文件:这是一个用C/C写的扩展文件,用dlopen()方法加载。json文件:被fs模块读取后,返回的结果被JSON.parse()解析。所有其他扩展都作为。js文件。每个加载的模块都将被缓存,并可以通过require.cache查看
使用ES-Module
目前在node中使用ES-Module是一个实验函数,从8.5开始就支持了,执行时需要添加- experimental-modules参数。从12.17.0 LTS开始,实验模块已经被删除,现在它可以以两种方式使用:使用。mjs文件,而不是。或者在package.json中将类型指定为模块
//package.json
{
名称: esm项目,
版本: 1.0.0 ,
main: index.js ,
类型:模块,
.
}与CommonJS的模块机制相比,ES-Module最大的不同在于,ES-Module动态引用导出模块的变量和对象,在编译阶段就公开了模块的导入接口,因此可以进行静态分析;CommonJS-Module在运行时同步加载,输出是导出模块的浅层副本。另外,ES-Module支持加载CommonJS-Module,反之亦然。
其次,Node规定一些CommonJS模块特有的内部变量不能在ES6模块中使用,因为ES-Module的顶层this指向undefined,CommonJS模块的顶层this指向当前模块,这些内部变量可以直接作为顶层变量使用。
CommonJS的内部变量是:
argumentsrequiremoduleexportsm _ _ filename _ _ dirname
总结
node模块同步加载,只有在加载完成后才能执行以下操作。
每个文件都是一个有自己范围的模块。在每个模块内部,module对象表示当前模块,其exports属性充当当前模块的导出接口。
导入的模块是导出模块的浅表副本。
有关编程的更多信息,请访问:编程视频!即CommonJS模块规范是什么?更多Nodejs模块机制分析详情请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。