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

理解import、require、export、module.export

时间:2023-04-04 00:25:20 Node.js

理解import、require、export、module.exportES6的模块设计模块设计的思想是尽量做到静态,这样模块之间的关系输入可以在编译和输出变量时确定。CommonJS和AMD都只能在运行时确定这些东西。commonJS模块是一个对象,输入的时候需要查找对象属性。//CommonJS模块let{stat,exists,readFile}=require('fs');//等价于let_fs=require('fs');letstat=_fs.stat;letexists=_fs.exists;letreadfile=_fs.readfile;nodeJS中的模块化使用了CommonJS规范,本质是将fs模块作为一个整体加载并生成fs_对象,读取对象上的属性和方法,这种加载方式就是“运行时加载”ES6模块ES6模块是不是对象,而是通过export命令显式指定输出代码,然后通过import命令导入。//ES6模块import{stat,exists,readFile}from'fs';上面代码的本质是从fs模块加载3个方法,并没有加载其他方法。这种加载称为“编译时加载”或静态加载,即ES6可以在编译时完成模块加载,比CommonJS模块加载效率更高。导出命令一个模块是一个独立的文件。文件内部的所有变量都无法从外部获得。如果想让外部能够读取模块内部的变量,就必须使用export关键字来输出变量。//profile.jsexportvarfirstName='Michael';exportvarlastName='Jackson';exportvaryear=1958;//profile.jsvarfirstName='Michael';varlastName='Jackson';varyear=1958;export{名字,姓氏,年份};export的语法,即外部导出接口,建立了接口名称与模块内部变量的一一对应关系。//Writing1exportvarm=1;//Writing2varm=1;export{m};//Writing3varn=1;export{nasm};importcommand注意导入命令有改进effect,会提升到整个模块的头部,先执行。现阶段通过Babel转码,可以将CommonJS模块的require命令和ES6模块的import命令写在同一个模块中,但最好不要这样做。因为import是在静态分析阶段执行的,所以是一个模块中最早执行的。以下代码可能没有预期的结果。require('core-js/modules/es6.symbol');require('core-js/modules/es6.promise');importReactfrom'React';exportdefaultcommandexportdefault命令用于指定默认模块的输出。显然,一个模块只能有一个默认输出,所以exportdefault命令只能使用一次。所以import命令后面不需要用大括号,因为它只能对应exportdefault命令。exportdefault本质上就是导出一个名为default的变量或方法,然后系统允许你给它取任何名字。因此,下面的写法是有效的。//modules.jsfunctionadd(x,y){returnx*y;}export{addasdefault};//等同于//exportdefaultadd;//app.jsimport{defaultasfoo}from'modules';//等价于//importfoofrom'modules';//Correctexportvara=1;//Correctvara=1;exportdefaulta;//Incorrectexportdefaultvara=1;CommonJS规范中每个文件都是一个模块,有自己的作用域。文件中定义的变量、函数和类都是私有的,对其他文件不可见。CommonJS规范规定,在每个模块内部,模块变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是一个外部接口。加载模块实际上是加载模块的module.exports属性。varx=5;varaddX=function(value){returnvalue+x;};module.exports.x=x;module.exports.addX=addX;//使用varexample=require('./example.js');console.log(example.x);//5console.log(example.addX(1));//6CommonJS模块特性所有代码都运行在模块作用域内,不会污染全局作用域。模块可以多次加载,但第一次加载时只会运行一次,然后缓存运行结果,后面加载时直接读取缓存的结果。要使模块再次工作,必须清除缓存。加载模块的顺序,按照它们在代码中出现的顺序。exportvarfoo='bar';setTimeout(()=>foo='baz',500);ES6将上述代码模块化,输出变量foo,其值为bar,500毫秒后变为baz。这与CommonJS规范完全不同。CommonJS模块输出值的缓存,并且没有模块对象的动态更新。Node内部提供了一个Module构造函数。所有模块都是模块的实例module.id模块的标识符,通常是带有绝对路径的模块文件名。module.filename模块的文件名,带有绝对路径。module.loaded返回一个布尔值,指示模块是否已完成加载。module.parent返回一个代表调用该模块的模块的对象。module.children返回一个数组,表示该模块将使用的其他模块。module.exports表示模块导出的值。module.exports属性module.exports属性表示当前模块的外部输出接口。当其他文件加载模块时,它实际上读取module.exports变量。为了方便起见,Node为每个模块提供了一个exports变量,指向module.exports。这相当于在每个模块的顶部都有一行这样的命令。varexports=module.exports;的结果就是当模块接口对外导出时,可以在exports对象中添加方法。exports.area=function(r){returnMath.PI*r*r;};exports.circumference=function(r){return2*Math.PI*r;};注意不能直接将exports变量指向一个值,因为这相当于切断了exports和module.exports之间的联系。exports.hello=function(){return'hello';};module.exports='你好世界';在上面的代码中,hello函数不能被外部导出,因为module.exports已经被重新赋值了。这意味着如果一个模块的对外接口是单值,则不能使用exports导出,只能使用module.exports导出。目录的加载规则与node中模块的加载规则一致。通常,我们会将相关文件放在一个目录中,以便于组织。这时候最好给目录设置一个入口文件,这样require方法就可以通过这个入口文件加载整个目录。在该目录下放置一个package.json文件,将入口文件写入main字段。下面是一个例子。//package.json{"name":"some-library","main":"./lib/some-library.js"}require发现参数字符串指向一个目录,会自动检查包在那个目录。json文件,然后加载main字段指定的入口文件。如果package.json文件没有main字段,或者根本没有package.json文件,则会加载该目录下的index.js文件或index.node文件。参考CommonJSSpecificationModule的语法