node.js 模块,nodejs的核心模块有哪些

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: