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

Nodejs端模块化方法comomjs详解

时间:2023-04-03 14:53:45 Node.js

nodejs端模块化通常是通过commonjs,使用模块化可以复用js代码,使逻辑结构更加清晰。commonjs的语法规则如下通过module.exports或exports导出,通过require函数导入//a.jsexportcontentconstname='alice'constage=16module.exports={name,age}//b.jsimportconstobj=require('./a.js')console.log(obj)//{name:'alice',age:16}module.exports和exports以不同的方式导出//a.jsconstname='alice'constage=16exports.age=agemodule.exports.name=name//b.jsconstobj=require('./a.js')console.log(obj)//{name:'kiki',age:16}两者的区别在于即使通过exports导出。给module.exports赋值,所以exports.xxx的形式相当于在module.exports对象中添加子元素。如果直接给module.exports赋值一个新对象,结果会不一样//a.jsconstname='alice'constage=16consthobby='singing'exports.name=nameexports.age=agemodule.exports={hobby}//b.jsconstobj=require('./a.js')console.log(obj)//{hobby:'singing'}重新赋值给module.exports一个对象,module.exports指向的内存空间不再和exports一样,所以最后export只有module的部分.exports为什么说是exportsassignedtomodule。出口而不是反向分配。一方面可以从源码中看到赋值过程。另一方面,您也可以通过更改导出方式来查看它。如果require最终导入的内容是由exports决定的,那么此时的输出应该是一个函数//a.jsconstsayHi=function(){console.log('hi')}exports={sayHi}//b.jsconstobj=require('./a.js')console.log(obj)//{}这时候只输出一个空对象,所以require的导出内容不是exports而是当module.exports是初始化后,exports和module.exports都指向一个空对象的内存地址。当exports直接添加empty对象的属性时,内存地址不会改变,而modulex.exports导出的内容可以随着exports的赋值而改变。但是这里当exports再次指向另一个对象时,module.exports还是指向原来的对象,module.exports没有变化,所以导出的内容还是一个空对象。了解了export部分,我们再来看看requireimport。通过require查找资源时,会有如下规则1、import是内置模块,如fs、http。这时候会直接搜索内置模块2.importedmodules包含路径,比如./or../or/./表示当前路径,../表示上层路径,/表示计算机的根目录。如果有路径,则根据路径查找。这时候有两种情况,一种是作为文件,一种是文件夹(1)如果文件有后缀,就会去搜索带有后缀的文件。如果没有后缀,则按照file/file.js/file.json/file.node的顺序搜索(2)文件夹依次搜索文件夹的index.js/index.json/index.node。如果找不到路径,会报错NotFind3。导出既不是内置模块也不是路径。然后它会从当前目录下的node_modules开始,逐层查找,看是否有对应的import文件。如果没有找到,会报错NotFind。那么require的模块加载流程是怎样的呢~首先只要使用require函数加载资源,就必须执行一次//a.jsletname='kiki'exports.name=nameconsole.log('a.js文件被执行')//b.jsrequire('./a.js')//如果a.js文件被执行,如果多个文件导入同一个文件,则文件不会被加载多个次。模块中有一个loaded字段来判断资源是否加载。在下图中b.js中打印的模块中,可以看到子元素a.js文件的loaded变为true,因为print语句在require之后,commonjs是同步执行的,所以a.js已加载。打印的时候,b.js还没有加载,如果loaded为false,不用担心会被重复调用。如果出现以下嵌套调用,文件将按照深度优先算法执行。首先从main对应的第一个顶点开始,逐层向下查找。找到底部后,向上搜索一层。查找完上一层的元素后,再向上查找一层,即main-->aaa-->ccc-->ddd--->eee。此时eee没有指向顶点,所以返回ddd,ddd没有指向除eee以外的顶点,然后返回ccc,以此类推,直到返回main,发现main也指向到另一个顶点bbb,所以执行bbb,bbb指向ccc和eee,但是因为这两个已经加载过,所以不会重新加载,最后的执行顺序是mainaaacccdddeeebbb另外还有几个特点commonjs执行的实现1.同步加载,每次执行js代码需要下载js文件,服务端处理文件一般都是执行本地文件,对性能影响不大,但是如果用在浏览器端,代码的同步执行会导致后面的js执行,造成阻塞,导致加载资源的时间变长,所以commonjs多用在服务端。//a.jsletname='kiki'exports.name=nameconsole.log('a.js文件被执行')//b.jsrequire('./a.js')console.log('b.js文件Executed')//a.js文件被执行b.js文件被执行javascript代码被解析成字节码,然后被编译器执行。运行时解析是指commonjs代码会在函数调用后执行。导入导出可以使用一些条件判断语句或者动态路径//a.jsletname='kiki'module.exports={name}//b.jsconstpath='./a.js'constflag=trueif(flag){const{name}=require(path)console.log(name)//kiki}3、module.exports和require是同一个对象,也就是说如果直接改module.exports.xxx,里面的内容require也会变,修改require,module.exports的内容也会变,下面演示如何修改导出的内容//a.jsletname='kiki'setTimeout(()=>{module.exports.name='hi'},1000)module.exports={name}//b.jsconstobj=require('./a.js')console.log(obj)setTimeout(()=>{console.log(obj)},2000)//执行顺序为{name:'kiki'}{name:'hi'}以上就是commonjs在nodejs中的使用详解。Commonjs是node模块化的一个非常重要的部分。理解了才能更好的应用~下一篇文章将介绍浏览器端es模块常用的模块化方法