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

node、es6模块使用对比exports、require、import

时间:2023-04-03 13:51:24 Node.js

node和es6模块使用对比目前使用js成为密不可分的模块,现在最常见的模块就是node采用的COMMONjs方法和es6规范,这里做个对比两种用法,不深入源码完全推导,node--commonjs标准模块化节点模块比较常见,它是全局变量global中的一个属性,文件和模块是一一对应的(Each文件被认为是一个独立的模块)。目前比较规范的是一个文件就是一个模块,主要通过exports和require来处理。exportsexports变量在模块的文件级作用域内有效,在模块执行前被赋值为module.exports的值。它有一个快捷方式,因此module.exports.f=...可以更简洁地写为exports.f=...。请注意,与任何变量一样,如果将新值分配给exports,则它不再绑定到module.exports。具体可以看代码:functionadd(x,y){returnx+y;}functionmultiply(x,y){returnx*y;}exports.add=(x,y)=>x+是;//exports作为module.exports的快捷方式exports.multiply=(x,y)=>x*y;module.exports=添加;//此时exportsmodule.exports被重新赋值为一个新的对象或函数(function也是一个对象)exports={add:add}//这个exports与module.exports无关,变成了一个独立模块作用域内的对象,不会被导出module.exports={//最后的导出部分add:add,multiply:multiply};exports和module.exports的关系,看下面代码的类似实现,好像实参和形参是一样的,不同的是如果没有module.exports的直接copy操作,会是指定为出口。functionrequire(/*...*/){constmodule={exports:{}};((module,exports)=>{//模块代码在这里。在这个例子中,定义了一个函数。functionsomeFunc(){}exports=someFunc;//此时,exports不再是模块的快捷方式。exports,//这个模块仍然导出一个空的默认对象。module.exports=someFunc;//此时,模块导出someFunc,而不是默认对象。})(module,module.exports);returnmodule.exports;}require一旦定义了模块,就应该使用它,vartest=require('./moduleTest.js');console.log(测试(1,2));console.log(mutiply(1,2));使用起来比较简单,使用变量获取导出的对象exports,然后就可以使用对象中的方法,我们有的时候可能会遇到这样的情况。你不需要使用变量来接受模块的导出,你可以直接使用它们。这种情况一般在模块代码导出时处理:module.exports.add=global.add=(x,y)=>x+y;将模块的方法直接提升到全局范围并不是一个好的选择。模块对象的一些解释Node使用两个核心模块来管理模块依赖关系:require模块,它们看起来像是在全局范围内可用的模块——不需要require('require')。module模块,看起来也可以在全局范围内使用——不需要require('module')。require模块导出的主要对象是一个函数(如上例中所用)。当Node以本地文件路径作为函数的唯一参数调用require()函数时,Node将执行以下步骤:解决:找到文件的绝对路径。Load:判断文件内容的类型。封装:为文件提供私有范围。这使得require和module对象都下载我们需要的每个文件。评估:这是虚拟机需要对加载的代码做的最后一件事。缓存:当我们再次需要这个文件时,不要重复所有的步骤。模块在第一次加载后被缓存。这也意味着(像其他缓存机制一样)如果对require('foo')的每次调用都解析到同一个文件,则返回同一个对象。多次调用require(foo)不会导致模块的代码被多次执行。这是一个重要的特征。有了它,可以返回“部分完成”的对象,允许加载传递依赖,即使它们导致循环。模块根据其解析的文件名进行缓存。模块可能会根据调用位置(例如从node_modules目录加载)解析为不同的文件名,因此不能保证require('foo')将始终返回完全相同的对象。此外,在不区分大小写的文件系统或操作系统上,不同解析的文件名可以指向同一个文件,但缓存仍会将它们视为不同的模块并重新加载它们多次。例如,require('./foo')和require('./FOO')返回两个不同的对象,无论./foo和./FOO是否是同一个文件。es6的模块在ES6之前,要使用一个模块,必须使用require函数导入一个模块,但是ES6并没有采用这种模块化的方案。在ES6中,使用import命令引入一个模块或者模块中的部分接口,并没有将require写入标准,也就是说require对于ES6代码来说只是一个普通的函数。同样,在ES6标准中,导出模块的接口只能使用export命令,不能使用exports对象,这也意味着module.exports只是node、requirejs等模块化库的自定义变量,并不是ES标准界面。当然,ES6的模块化也很复杂,不仅仅是模块接口的导入导出,这篇文章厚着脸皮只讨论这个比较常见的问题。导出使用先列举一些常用的使用方法:exportfunctionfun(){};导出{名称1,名称2,…,名称N};export{variable1asname1,variable2asname2,…,nameN};exportletname1,name2,…,nameN;//也是varexportletname1=…,name2=…,…,nameN;//也是var,constexport默认表达式;exportdefaultfunction(…){…}//也是class,function*exportdefaultfunctionname1(…){…}//也是class,function*export{name1asdefault,…};export*from…;export{name1,name2,…,nameN}from…;export{import1asname1,import2asname2,…,nameN}from…;导出不是变量的变量是绝对错误的,包括导出表达式,也是绝对错误的。export允许多个export{}形式,看起来很奇怪,其实和react的setState是一样的。增量添加模式。另外,ES6比较强大的是变量导出后可以继续修改。使用import导入实际上比requireimport{a,obj}from'./module-file'更舒服;与node之前的require不同,require只能将模块放入一个变量中,但是在ES6中,它具有解构和赋值对象的能力,所以直接将导入模块的接口赋值给变量。内部机制也不同。require需要执行整个模块,并将整个模块放在内存中(也就是我们称之为runtime的时候)。如果只使用其中一个方法,性能会差很多,而import...from只会加载需要的接口方法,其他方法在程序启动后不能触及,所以这也叫“编译时”",而且它的性能要好得多。ASDEFAULT*关键字编程同学很容易理解as,简单的说就是取一个别名。可以用在上面的export中,也可以用在import中://a.jsvara=function(){};export{aasfun};//b.jsimport{funasa}from'./A';A();而default是一种语法糖,做法类似://d.jsexportdefaultfunction(){}//等价于:functiona(){};export{aasdefault};//在导入时,可以这样使用:importafrom'./d';//相当于,或者是下面写法的简写,同义import{defaultasa}from'./d';意思是把所有的exports都赋值给newExport,后面加上这个别名对象,从而获得模块的所有方法。参考:http://www.tangshuang.net/288...https://juejin.im/entry/58d4e...http://nodejs.cn/api/modules....