当前位置: 首页 > 后端技术 > Node.js

require()方法详解

时间:2023-04-03 11:59:52 Node.js

在NodeJS中有一个方法是我们使用频率最高的,那就是require方法。NodeJs遵循CommonJS规范,其核心是通过require加载其他依赖模块。几个问题module.exportsorexports是全局变量吗?模块是同步加载还是异步加载?循环引用会不会导致性能问题或者导致bug?什么是CommonJS每个文件都是一个模块,有自己独立的作用域、变量、方法等,对其他模块不可见。CommonJS规范规定,在每个模块内部,模块变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。Nodemodules的分类内置模块-Nodejs中以C++提供的模块。constant模块-在Nodejs中定义常量的模块。nativemodule——Nodejs中以javascript形式提供的模块。第三方模块——第三方提供的模块。模块对象NodeJs内部提供了一个Module构造函数。所有模块都是Module的实例。在每个模块内部,都有一个表示当前模块的模块对象。它具有以下属性。模块对象的属性module.id模块的标识符,通常是带绝对路径的模块文件名。module.filename模块的文件名,带有绝对路径。module.loaded返回一个布尔值,指示模块是否已完成加载。module.parent返回一个对象,表示调用该模块的模块(程序入口文件的module.parent为null)module.children返回一个数组,表示该模块要使用的其他模块。module.exports表示模块导出的值。module.exports属性module.exports属性表示当前模块的外部输出接口。当其他文件加载模块时,它实际上读取module.exports变量。module.exports属性表示当前模块的对外输出接口。当其他文件加载模块时,它实际上读取module.exports变量。我们有时会这样写exports变量://test.jsfunctiontest(){console.log(test);}export.test=test;//result.jsconsttest=require("./test")得到正确的结果,这是因为:exports变量指向module.exports。这相当于在每个模块的顶部都有一行这样的命令。varexports=module.exports;注意:不能直接给exports变量赋值,这样会改变exports的方向,不再指向module.exports。给exports赋值是无法在其他模块中使用require方法获取到的,因为require方法获取的是其他模块的module.exports的值。建议:尽可能使用module.exports导出结果。使用模块require方法创建模块导出模块加载模块的过程require是节点用来加载和执行其他文件导出的模块的方法。在NodeJs中,我们导入的任何模块都对应一个Module实例,包括入口文件。完成步骤:调用父模块的require方法(父模块是指调用模块的当前模块)require=functionrequire(path){returnmod.require(path);};调用Module的_load方法,通过Module._resolveFilename获取模块路径文件名constfilename=Module._resolveFilename(request,parent,isMain);根据fileName判断是否有该模块的缓存。如果有缓存,调用updateChildren方法更新缓存内容并返回缓存。如果没有缓存,则继续作为Native模块执行,调用loadNativeModule方法加载。如果加载成功,则返回本机模块。否则继续执行。根据当前模块名(路径)和父模块对象生成一个Module实例:constmodule=cachedModule||newModule(filename,parent);判断模块是否为入口文件if(isMain){process.mainModule=module;module.id='.';}将模块实例存储在模块缓存中Module._cache[filename]=module;模块实例调用自己的load方法,根据文件名加载模块module.load(filename);获取模块文件的后缀名constextension=findLongestRegisteredExtension(filename);如果后缀名是ESModule格式(.mjs),则判断该Module是否支持解析.mjs文件,不支持则抛出异常。根据后缀名Module._extensions[extension](this,filename)解析模块文件内容;根据文件名读取文件内容content=fs.readFileSync(filename,'utf8');编译执行读取的文件,调用模块本身的_complile方法:module._compile(content,filename);_编译主要内容步骤:constcompiledWrapper=wrapSafe(filename,content,this);constdirname=path.dirname(文件名);constrequire=makeRequireFunction(this,redirects);letresult;constexports=this.exports;constthisValue=exports;constmodule=this;result=compiledWrapper.call(thisValue,exports,require,module,filename,dirname);返回结果;具体获取wrapSafe方法的返回值上面结果的代码为:constwrapper=Module.wrap(content);returnvm.runInThisContext(wrapper,{filename,lineOffset:0,displayErrors:true,importModuleDynamically:async(specifier)=>{constloader=asyncESM.ESMLoader;returnloader.import(specifier,normalizeReferrerURL(filename));},});修改模块加载状态为truethis.loaded=true;加载成功。总结通过上面的调试过程,可以得出以下结论:在NodeJs中,从入口文件开始,一切都是Module。模块加载是同步的。由于缓存机制的存在,模块的循环引用对性能影响不大,循环引用的模块可能不完整,并可能导致错误。结束~学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!大家好!我是〖编程三昧〗的作者汪山人,我的公众号是《编程三昧》,欢迎关注,希望大家多多指教!知识与技能并重,内功与外功并重,理论与实践两手抓,两手都要用力!