node.js 模块,nodejs的核心模块有哪些
本文带你了解Nodejs中的模块系统,希望对你有所帮助!
node.js速度课程简介:进入学习
模块化的背景
早期的JavaScript是为了实现简单的页面交互逻辑。但是随着时代的发展,浏览器已经不能仅仅呈现简单的交互,各类网站开始大放异彩。随着网站的复杂化,前端代码越来越多,与其他静态语言相比,JavaScript缺乏模块化的弊端,比如命名冲突,就暴露出来了。因此,为了方便前端代码的维护和管理,社区开始定义模块化规范。在这个过程中,有很多模块化的规范,比如CommonJS,AMD,CMD,ES模块。本文主要阐述基于CommonJS的节点模块化。
CommonJS 规范
首先,在节点世界中,模块系统遵从定义它的CommonJS规范。简单来说:
每个文件都是一个模块。模块的信息由模块对象表示。模块公开的信息由exports导出。需求引用了一个模块
Node 模块分类
。核心模块,比如fs,http,path等。不需要安装。这些模块在运行时已经加载到内存中。【推荐学习:《nodejs 教程》】第三方模块:通过安装存储在node_modules中。自定义模块:主要指文件模块,通过绝对路径或相对路径引入。
Module 对象
我们上面说过,文件是一个模块,当前的模块信息是由一个模块对象来描述的,它具有以下属性:
-id:当前模块的id
-path:当前模块对应的路径。
-exports:当前模块公开的变量。
-parent:也是module对象,表示当前模块的父模块,即调用当前模块的模块。
-filename:当前模块的文件名(绝对路径),可以用来在模块引入时将加载的模块添加到全局模块缓存中,后续的引入会直接从缓存中取值。
-loaded:指示当前模块是否已加载。
-children:是保存当前模块调用的模块的数组。
-paths:这是一个数组,记录了从当前模块开始搜索node_modules目录,并递归搜索到根目录中的node_modules目录。
module.exports 与 exports
说完了CommonJS规范,再来说说module.exports和exports的区别。
首先,我们使用一个新的模块进行简单的验证。
console . log(module . exports===exports);//true可以发现module.exports和epxorts实际上指向同一个引用变量。
demo1
//一个模块
module . exports . text= XXX ;
exports . value=2;
//b模块代码
设a=require(。/a’);
console . log(a);//{text: xxx ,value: 2}这样就验证了在上面的demo1中,为什么要通过module.exports和exports来添加属性,这两者都是在引入模块时存在的,因为两者最终都是将属性添加到同一个引用变量中。根据这个演示,可以得出结论,module.exports和exports指向同一个引用变量。
demo2
//一个模块
模块.导出={
文本:“xxx”
}
exports . value=2;
//b模块代码
设a=require(。/a’);
console . log(a);//{text: xxx}在上面的演示示例中,重新分配了module.exports,并添加了导出的属性。但是,在引入模块之后,由module.exports定义的值最终被导出。可以得出结论,noed的模块最后导出了module.exports,而exports只是对module.exports的引用,类似于下面的代码:
exports=module . exports={ };
(函数(导出,模块){
//模块中的代码
模块.导出={
文本:“xxx”
}
exports . value=2;
console . log(module . exports===exports);//假
})(exports,module)因为在函数的执行中,exports只是对原module.exports对应的变量的引用,当module.exports赋值后,exports对应的变量与最新的module.exports并不相同。
require 方法
将需求引入模块的过程主要分为以下步骤:
将文件路径解析为绝对路径,查看当前要加载的模块是否有缓存。如果有缓存,可以直接用缓存看看是不是node自带的模块,比如http,fs等。如果是,可以直接返回根据文件路径创建一个模块对象,将模块添加到模块缓存中,通过相应的文件解析方法解析编译文件(node只支持解析。js,json,节点后缀文件)并返回加载的模块导出对象。
module . prototype . require=function(id){
//.
尝试{
//主要通过模块的静态方法_load来加载模块。
返回模块。_load(id,this,/* isMain */false);
}终于{}
//.
};
//.
模块。_load=function(request,parent,isMain) {
let relResolveCacheIdentifier
//.
//将文件路径解析为绝对路径
const文件名=模块。_resolveFilename(request,parent,is main);
//检查当前要加载的模块是否已经有缓存。
const cachedModule=模块。_ cache[文件名];
//如果有缓存,就用缓存的。
if (cachedModule!==未定义){
//.
返回cachedModule.exports
}
//检查是否是节点本身的模块,比如http,fs等。如果有,直接退掉。
const mod=loadNativeModule(文件名,请求);
if (mod mod.canBeRequiredByUsers)返回mod.exports
//根据文件路径初始化模块
const module=cachedModule 新模块(文件名,父);
//.
//将此模块添加到模块缓存中
模块。_ cache[文件名]=模块;
如果(家长!==未定义){
relatieresolvecache[relResolveCacheIdentifier]=filename;
}
//.
//加载模块。
module.load(文件名);
返回模块. exports;
};至此,node的模块原理流程基本完成。目前node v13.2.0已经正式支持ESM特性。
__filename, __dirname
在与node的接触中,你是否困惑于_ _ _ _ filename,__dirname是从哪里来的,为什么会有这些变量?仔细阅读这一章,你会对这些有一个系统的了解。
遵循上述要求的源代码。模块加载时,会读取模块的内容,将内容包装成函数体,函数执行后将拼接的函数字符串编译成函数,并传入相应的参数module . prototype . _ compile=function(content,filename) {
//.
const compiledWrapper=wrapSafe(文件名,内容,this);
//
result=compiled wrapper . call(this value,exports,require,module,
文件名,目录名);
//.
返回结果;
};函数wrapSafe(文件名,内容,cjsModuleInstance) {
//.
const wrapper=module . wrap(content);
//.
}
let wrap=函数(脚本){
返回Module.wrapper[0]脚本module . wrapper[1];
};
常量包装=[
(function (exports,require,module,__filename,__dirname) { ,
\ n });”
];
ObjectDefineProperty(模块,“wrap”,{
get() {
回绕;
},
设置(值){
patched=true
wrap=value
}
});综上所述,变量_ _ dirname、_ _ filename、module、exports、require都在模块中,实际上是node在执行过程中引入的。看完之后,你能解开多年的谜团吗_
NodeJS 中使用 ES Modules
在package.json中添加“类型”:“模块”配置//test.mjs
导出默认值{
答:“xxx”
}
//import.js
从导入。/test . mjs ;
console . log(a);//{a: xxx}
import 与 require 两种机制的区别
明显的区别在于执行时机:
ES在执行时会导入所有导入的模块。模块将首先被预解析,并在模块中的其他模块之前执行//entry.js。
console.log(执行条目);
设a=require(。/a . js’)
console . log(a);
//a.js
console . log(-a-);
module.exports=这是一个;
//最终的输出序列是:
//执行条目
//- a -
//这是一个//entry.js
console.log(执行条目);
从导入b。/b . mjs ;
console . log(b);
//b.mjs
console . log(-b-);
导出默认值“这是b”;
//最终的输出序列是:
//- b -
//执行条目
//这是bimport只能在模块的顶层,不能在代码块中(比如if代码块)。如果需要动态引入,需要使用import()动态加载;与CommonJS模块相比,ES模块有以下不同之处:
不需要导出或模块导出
大多数情况下,可以使用es模块导入来加载CommonJS模块。(CommonJS模块文件后缀是cjs)
如果您需要一个带有。js后缀,可以使用module.createRequire()在ES模块中构造Require函数。
//test.cjs
导出默认值{
答:“xxx”
}
//import.js
从导入。/test . cjs ;
console . log(a);//{a: xxx}//test.cjs
导出默认值{
答:“xxx”
}
//import.js
从导入。/test . cjs ;
console . log(a);//{a: xxx}//test.cjs
导出默认值{
答:“xxx”
}
//import.mjs
从“模块”导入{ create require };
const require=create require(import . meta . URL);
//test.js是一个CommonJS模块。
const siblingModule=require(。/test’);
console . log(sibling module);//{a: xxx}没有__filename或__dirname
这些CommonJS变量在es模块中不可用。
没有加载JSON模块
JSON导入仍处于实验阶段,仅受-experimental-JSON-modules-modules标志的支持。
没有要求。解决
没有节点路径
没有必要的扩展
没有require.cache
ES 模块和 CommonJS 的相互引用
在 CommonJS 中引入 ES 模块
因为ES模块的加载、解析和执行是异步的,require()的过程是同步的,所以不能通过require()引用一个ES6模块。
ES6提出的import()函数将返回一个Promise,它标志着ES模块加载完成。这样,我们就可以用异步方式在CommonJS中导入ES模块:
//b.mjs
导出默认值 esm b
//entry.js
(async ()={
let { default:b }=wait import(。/b . mjs’);
console . log(b);//esm b
})()在 ES 模块中引入 CommonJS
在ES6模块中使用import引用CommonJS模块很方便,因为在ES6模块中不需要异步加载:
//a.cjs
module.exports= commonjs a
//entry.js
从导入。/a . cjs ;
console . log(a);//commonjs a在这一点上,提供两个demo给大家,测试以上知识点是否掌握。如果没有,可以回去读。
demo module.exportsexports
//一个模块
exports . value=2;
//b模块代码
设a=require(。/a’);
console . log(a);//{value: 2}demo module.exportsexports
//一个模块
出口=2;
//b模块代码
设a=require(。/a’);
console . log(a);//{}require_cache 模块缓存机制
//source . js
设count=0;
exports.addCount=function () {
数数
}
exports.getCount=function () {
返回计数;
}
//b.js
let { getCount }=require(。/origin’);
exports . get count=get count;
//a.js
设{ addCount,getCount: getValue }=require(。/origin’);
add count();
console . log(getValue());//1
let { getCount }=require(。/b’);
console . log(getCount());//1
require.cache
根据上面的例子,引入require时,模块将被添加到缓存对象require.cache中。如果需要删除缓存,可以考虑清除缓存内容,require模块下次会重新加载该模块。
设count=0;
exports.addCount=function () {
数数
}
exports.getCount=function () {
返回计数;
}
//b.js
let { getCount }=require(。/origin’);
exports . get count=get count;
//a.js
设{ addCount,getCount: getValue }=require(。/origin’);
add count();
console . log(getValue());//1
delete require . cache[require . resolve(。/origin )];
let { getCount }=require(。/b’);
console . log(getCount());//0
结语
到目前为止,本文主要介绍了Node中基于CommonJS的模块化机制,并借助源代码分析了模块化的全过程。关于模块的介绍,请参考以下参考资料。如果你有任何问题,请在评论区留言。谢谢你。
参考资料
CommonJS模块
ES模块
有关编程的更多信息,请访问:编程视频!以上文章是对Nodejs中模块系统细节的快速了解。请多关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。