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

commonJs、AMD、UMD、es6模块化的区别,这篇就够了

时间:2023-04-03 15:24:41 Node.js

commonJs、A??MD、UMD、es6模块化的区别,这篇文章就够了,模块化思想模块化就是把逻辑代码拆分成独立的块,各自封装,相互独立。每个块决定向外界暴露什么,同时决定导入和执行哪些外部代码。2、commonJsNode应用由模块组成,采用CommonJS模块规范。也就是说,CommonJs是应用在node服务器端的。如果浏览器要使用CommonJs规范,需要使用browserify库进行转换。(后面会有例子)CommonJs分为moudle对象和requeire命令两部分1、moudle对象Node内部提供了一个Module构造函数。所有模块都是ModulefunctionModule(id,parent){this.id=id;的实例this.exports={};this.parent=parent;//...}在每个模块中,都有一个模块对象,代表当前模块。它具有以下属性。module.id模块的标识符,通常是带有绝对路径的模块文件名。module.filename模块的文件名,带有绝对路径。module.loaded返回一个布尔值,指示模块是否已完成加载。module.parent返回一个代表调用该模块的模块的对象。module.children返回一个数组,表示该模块将使用的其他模块。module.exports表示模块导出的值。module.exports属性表示当前模块的对外输出接口。当其他文件加载模块时,它实际上读取module.exports变量。//a.jsmodule.exports={name:'ysl',age:'27'}letobj=require('./a.js')console.log(obj)//{name:'ysl',age:27}除了使用module.exports导出值,还可以使用exports导出//a.jsexport.name='ysl'export.age=27//注意export={name:'ysl',ageisnotallowed:27},这个方法无效,上面两种方法的结果是一致的2.上面的require命令我们已经导出了内容,现在如何加载导出的内容,node内置的require命令用于加载模块文件//a.jsmodule.exports={name:'ysl',age:27}//b.jsletobj=require(./a.js)console.log(obj)//{name:'ysl',age:27}需要注意一点:当require第一次加载一个模块时,Node会缓存这个模块。稍后加载模块时,直接从缓存中获取模块的module.exports属性。//a.jsmodule.exports={name:'ysl',age:27}//b.jsletobj1=require('./a.js')console.log(obj)//{name:'ysl',age:27}obj1.name='abc'letobj2=require('./test')console.log(obj2)//{name:'abc',age:27}console.log(obj1)//{name:'abc',age:27}//从这个例子可以看出node会缓存模块deleterequire.cache[require.resolve('./a.js')]letobj3=require('./test')console.log(obj3)//{name:'ysl',age:27}//可以使用deleterequire.cache[moduleName]删除缓存。注2:CommonJS模块的加载机制是,输入是输出值的副本。也就是说,一旦输出了一个值,模块内部的变化就不会影响这个值。//lib.jsvarcounter=3;functionincCounter(){counter++;}module.exports={counter:counter,incCounter:incCounter,};//main.jsvarcounter=require('./lib').counter;varincCounter=require('./lib').incCounter;console.log(计数器);//3incCounter();console.log(计数器);//3//上面的代码表明,`counter`输出后,`lib.js`模块内部的变化不会影响`counter`。3.由于AMD是同步加载commonJs的,如果一个模块在浏览器中加载时间过长,整个应用就会停在那里等待,页面就会卡顿。(下面的例子)            //首先,加载时,浏览器会停止渲染网页。加载的文件越多,网页失去响应的时间就越长;其次,由于js文件之间的依赖关系,需要严格保证加载顺序(比如上例中的1.js应该在2.js的前面),最依赖的模块必须最后加载。当依赖关系复杂时,代码编写和维护都会变得困难AMD采用异步方式加载模块,模块的加载不影响后面语句的运行。所有依赖该模块的语句都定义在一个回调函数中,回调函数只有在加载完成后才会运行。require.js类库实现了AMD规范。先说说它的基本用法:1.下载require.js:npminstallrequirejs2。quoterequire.js//data-main属性的作用是指定主模块的网络程序。上面的例子中是js目录下的index.js,这个文件会被require.js先加载。由于require.js默认的文件后缀是js,所以可以写index3和index.js模块代码。主模块是依赖它的其他模块的入口。这时候就需要使用AMD规范定义的require()函数来加载依赖模块。 //index.js require(['moduleA','moduleB','moduleC'],function(moduleA,moduleB,moduleC){    //这里有一些代码//可以写在这里模块加载后的代码 });//require()函数接受两个参数://第一个参数是一个数组,表示依赖的模块['moduleA','moduleB','moduleC']//第二个参数是一个回调函数,会被调用当前面指定的模块加载成功时//注意['moduleA','moduleB','moduleC']中的三个模块和索引。js在同一个目录4.如何写模块,AMD提供了define方法,调用define并传入一个函数//moduleA.js  define(function(){    varadd=function(x,y){      returnx+y;    };    return{      add:add    };  });//index.jsrequire(['moduleA'],function(moduleA){    console.log(moduleA)//moduleA是moduleA.js传入的函数执行后返回的对象{add:function} });以上是符合AMD规范的基本require.js用法3.UMD规范进一步处理了CommonJs、CMD、AMD。它没有自己专有的规范,而是集成了CommonJs、CMD、AMD的规范。它可以使相同的代码模块通过运行时或编译时运行在使用CommonJs、CMD甚至AMD的项目中。以后运行在浏览器端和服务器端的同一个JavaScript包只需要遵循相同的写法即可。//UMD简单实现((global,factory)=>{//如果当前上下文有define函数,AMD表示在AMD环境中if(typeofdefine==='function'&&define.amd){define(["moduleA"],factory);}elseif(typeofexports==='object'){//commonjsletmoduleA=require("moduleA")modules.exports=factory(moduleA)}else{全局.moduleA=factory(global.moduleA)//直接挂载为windows全局变量}})(this,(moduleA)=>{//这个模块的定义return{}})4.ES6模块历史上,JavaScript有没有模块(module)系统,不可能将一个大程序拆分成相互依赖的小文件,并以简单的方式将它们组装在一起。ES6在语言标准层面实现了模块功能,实现起来相当简单。它可以完全替代CommonJS和AMD规范,成为浏览器和服务器的通用模块方案。UMD模块格式不再需要,未来服务器和浏览器都会支持ES6模块格式。es6模块的语法分为两部分:exportmoduleexport,importmoduleimport1,export语法//profile.jsvarfirstName='Michael';varlastName='Jackson';varyear=1958;export{firstName,姓,年};上面的例子导出了一个对象,包括firstName、lastName、year三个属性。注意:export语句输出的接口与其对应的值具有动态绑定关系,即可以通过该接口获取模块内部的实时值。//profile.jsexportvarfoo='bar';setTimeout(()=>foo='baz',500);//上面代码输出变量`foo`,值为`bar`,500毫秒后变为`baz`2.importsyntax//main.jsimport{firstName,lastName,year}from'./profile.js';functionsetName(element){element.textContent=firstName+''+lastName;}上述代码的导入命令,它加载profile.js文件并从中导入变量。导入命令接受一对花括号,它指定要从其他模块导入的变量名。花括号内的变量名必须与导入模块(profile.js)的对外接口名相同。除了指定加载某个输出值外,还可以使用整体加载,即指定一个带星号(*)的对象,所有输出值都加载到这个对象上。//main.jsimport*astotalfrom'./profile.js';functionsetName(element){element.textContent=total.firstName+''+total.lastName;}3.导出默认语法可以从前面的例子中得到可以看出,在使用import命令时,用户需要知道要加载的变量或函数的名称,否则无法加载。但是,用户肯定希望快速上手,可能不愿意阅读文档来了解模块的属性和方法。为了给用户提供方便,让他们不用看文档就可以加载模块,exportdefault命令用于指定模块的默认输出。//profile.jsvarfirstName='Michael';varlastName='Jackson';varyear=1958;exportdefault{firstName,lastName,year};//main.jsimporttotalfrom'./profile.js';functionsetName(element){element.textContent=total.firstName+''+total.lastName;}注:exportdefault命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,所以exportdefault命令只能使用一次。//正是因为`exportdefault`命令实际上只输出了一个名为`default`的变量,所以后面不能跟变量声明语句。导出默认变量a=1;//会报错4.如果export和import在一个模块中复用,同一个模块先输入再输出,import语句可以和export语句一起写。export{foo,bar}from'my_module';//可以简单理解为import{foo,bar}from'my_module';export{foo,bar};5.import()使用场景import()返回一个Promise对象(1)按需加载,路由按需加载就是调用这个方法button.addEventListener('click',event=>{import('./dialogBox.js').then(dialogBox=>{dialogBox.open();}).catch(error=>{/*错误处理*/})});(2)条件加载if(condition){import('moduleA').then(...);}else{import('moduleB').then(...);}CommonsJs和ES6CommonJS模块输出的区别值的副本,ES6模块输出对值的引用CommonJS模块在运行时加载,ES6模块在编译时输出接口。CommonJS模块的require()是同步加载模块,ES6模块的import命令是异步加载的,有独立的模块依赖解析阶段。五、node和浏览器分别加载es6和commonJs的方式node加载es6模块的方式1.ES6模块使用.mjs后缀的文件名。2.在项目的package.json文件中,指定type字段为module。(注意:如果没有type字段,或者type字段是commonjs,.js脚本会被解释为CommonJS模块。)总结一句话:.mjs文件总是作为ES6模块加载,并且.cjs文件始终作为CommonJS模块加载,.js文件的加载取决于package.json中type字段的设置。Node默认采用commonJs模块模块//a.jsmodule.exports={name:'ysl}//main.jsletobj=require('./a.js')//node环境可以直接使用浏览器loades6浏览器以模块方式加载ES6模块,同样使用//浏览器异步加载带有`type="module"`的``标签的`defer`属性。//通过